Target Sum


Link: Target Sum – Leetcode


Problem Statement

You are given an integer array nums and an integer target. You want to build an expression using + and - operators on elements in nums such that the final result is equal to the target.

Return the number of ways to assign symbols to make the sum equal to target.


Intuition

We are to assign either a + or - sign to each number. This creates two subsets:

The total sum of array = S = S1 + S2
Required: S1 - S2 = target
→ So,

S1 - S2 = target  
S1 + S2 = sum  
-----------------  
2S1 = target + sum  
S1 = (target + sum)/2

So the problem reduces to: Count the number of subsets with sum = (target + sum) / 2

If (target + sum) is odd or target > sum, return 0.


Step 1: Recursion

Choice Diagram

At every index i, we have two choices:

IBH (Induction – Base – Hypothesis)

Function Signature:

int countWays(int index, int target, int[] nums)

Base Case:

Recurrence Relation:

int notPick = countWays(i-1, target, nums);
int pick = 0;
if(nums[i] <= target)
    pick = countWays(i-1, target - nums[i], nums);
return pick + notPick;

Code – Recursion:

class Solution {
    public int countWays(int index, int target, int[] nums) {
        if (index == 0) {
            if (target == 0 && nums[0] == 0) return 2;
            if (target == 0 || target == nums[0]) return 1;
            return 0;
        }

        int notPick = countWays(index - 1, target, nums);
        int pick = 0;
        if (nums[index] <= target)
            pick = countWays(index - 1, target - nums[index], nums);

        return pick + notPick;
    }

    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int num : nums) sum += num;

        if ((sum + target) % 2 != 0 || sum < Math.abs(target)) return 0;

        int S1 = (sum + target) / 2;
        return countWays(nums.length - 1, S1, nums);
    }
}

Step 2: Memoization (Top-Down DP)

Add a dp[i][target] array to store already computed results.

Code – Memoization:

class Solution {
    public int countWays(int index, int target, int[] nums, int[][] dp) {
        if (index == 0) {
            if (target == 0 && nums[0] == 0) return 2;
            if (target == 0 || target == nums[0]) return 1;
            return 0;
        }

        if (dp[index][target] != -1) return dp[index][target];

        int notPick = countWays(index - 1, target, nums, dp);
        int pick = 0;
        if (nums[index] <= target)
            pick = countWays(index - 1, target - nums[index], nums, dp);

        return dp[index][target] = pick + notPick;
    }

    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int num : nums) sum += num;

        if ((sum + target) % 2 != 0 || sum < Math.abs(target)) return 0;

        int S1 = (sum + target) / 2;
        int[][] dp = new int[nums.length][S1 + 1];
        for (int[] row : dp) Arrays.fill(row, -1);

        return countWays(nums.length - 1, S1, nums, dp);
    }
}

Step 3: Tabulation (Bottom-Up DP)

Code – Tabulation:

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int num : nums) sum += num;

        if ((sum + target) % 2 != 0 || sum < Math.abs(target)) return 0;

        int S1 = (sum + target) / 2;
        int n = nums.length;
        int[][] dp = new int[n][S1 + 1];

        // Base cases
        if (nums[0] == 0) dp[0][0] = 2; // +0 and -0
        else dp[0][0] = 1;

        if (nums[0] != 0 && nums[0] <= S1)
            dp[0][nums[0]] = 1;

        for (int i = 1; i < n; i++) {
            for (int j = 0; j <= S1; j++) {
                int notPick = dp[i - 1][j];
                int pick = 0;
                if (nums[i] <= j)
                    pick = dp[i - 1][j - nums[i]];
                dp[i][j] = pick + notPick;
            }
        }

        return dp[n - 1][S1];
    }
}

Step 4: Space Optimization

We only need values from the previous row → Use two 1D arrays.

Code – Space Optimized:

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int num : nums) sum += num;

        if ((sum + target) % 2 != 0 || sum < Math.abs(target)) return 0;

        int S1 = (sum + target) / 2;
        int n = nums.length;
        int[] prev = new int[S1 + 1];

        // Base case
        if (nums[0] == 0) prev[0] = 2;
        else prev[0] = 1;

        if (nums[0] != 0 && nums[0] <= S1)
            prev[nums[0]] = 1;

        for (int i = 1; i < n; i++) {
            int[] curr = new int[S1 + 1];
            for (int j = 0; j <= S1; j++) {
                int notPick = prev[j];
                int pick = 0;
                if (nums[i] <= j)
                    pick = prev[j - nums[i]];
                curr[j] = pick + notPick;
            }
            prev = curr;
        }

        return prev[S1];
    }
}

Time and Space Complexity


Conclusion

This problem is a disguised variation of subset sum count and emphasizes how transforming the problem using mathematical properties simplifies the logic significantly. You should always look for such transformations in DP problems.