150NeetCode 150

115.Target Sum

Medium2-D Dynamic Programming · 5/11

Problem

You are given an integer array nums and an integer target. Put either a '+' or a '-' sign in front of every number and concatenate them into an expression (e.g. +2 -1 +1).

Return the number of different sign assignments whose expression evaluates to target.

Examples

Input:  nums = [1, 1, 1, 1, 1], target = 3
Output: 5
Explanation: Exactly one number gets a '-' sign, and it can be any of the 5:
             -1+1+1+1+1 = 3, +1-1+1+1+1 = 3, ..., +1+1+1+1-1 = 3
Input:  nums = [1], target = 1
Output: 1
Input:  nums = [0, 0, 1], target = 1
Output: 4
Explanation: Each 0 can be +0 or -0 independently: 2 * 2 = 4 assignments.

Constraints

  • 1 <= nums.length <= 20
  • 0 <= nums[i] <= 1000
  • 0 <= sum(nums) <= 1000
  • -1000 <= target <= 1000

💡 Key Insight

Split the numbers into those with + (sum P) and those with - (sum N). Then P - N = target and P + N = total, so P = (total + target) / 2. The problem becomes "count subsets of nums that sum to P" — a classic 0/1 knapsack count solvable with a single 1-D array iterated backwards. If total + target is odd or |target| > total, the answer is 0.

Recursion (Brute Force)

TimeO(2^n)SpaceO(n)

Intuition

Try both signs for every number. dfs(i, sum) = number of ways to finish from index i when the running sum is sum.

Algorithm

  1. dfs(i, sum):
    • If i == n, return 1 if sum == target else 0.
    • Return dfs(i + 1, sum + nums[i]) + dfs(i + 1, sum - nums[i]).
  2. Return dfs(0, 0).

Visualization

flowchart TD
    A["dfs(0, 0)<br/>nums [1,1], target 0"] -->|"+1"| B["dfs(1, 1)"]
    A -->|"-1"| C["dfs(1, -1)"]
    B -->|"+1"| D["sum 2 → 0"]
    B -->|"-1"| E["sum 0 → 1"]
    C -->|"+1"| F["sum 0 → 1"]
    C -->|"-1"| G["sum -2 → 0"]
    E --> H["total = 2"]
    F --> H

Code

java
class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        return dfs(nums, target, 0, 0);
    }

    private int dfs(int[] nums, int target, int i, int sum) {
        if (i == nums.length) {
            return sum == target ? 1 : 0; // one full expression built
        }
        return dfs(nums, target, i + 1, sum + nums[i])   // '+' sign
             + dfs(nums, target, i + 1, sum - nums[i]);  // '-' sign
    }
}

Complexity

  • Time: O(2^n) — every sign combination is enumerated (n ≤ 20, so ~10^6 — this actually passes, but it scales badly).
  • Space: O(n) — recursion depth.

Complexity Comparison

Approach Time Space
Recursion (Brute Force) O(2^n) O(n)
Top-Down Memoization O(n · total) O(n · total)
Bottom-Up 2-D Tabulation O(n · total) O(n · total)
Subset-Sum 1-D DP O(n · total) O(total)

Common Pitfalls

  • Forgetting the parity check: if total + target is odd, no subset works — return 0 (otherwise integer division silently rounds).
  • Not checking |target| > total — P becomes negative (array size error) or exceeds total (index out of bounds in the 2-D table).
  • Iterating j upward in the 1-D knapsack — numbers get reused, overcounting.
  • Indexing memo by a raw negative sum — offset by total.
  • Treating 0s specially or skipping them — each zero doubles the count, and the DP already handles it.