Skip to content

Target Sum ​

Problem Statement ​

You are given an integer array nums and an integer target.

You want to build an expression out of nums by adding one of the symbols '+' and '-' before each integer in nums and then concatenate all the integers.

  • For example, if nums = [2, 1], you can add a '+' before 2 and a '-' before 1 and concatenate them to build the expression "+2-1".

Return the number of different expressions that you can build, which evaluates to target.

Input/Output Format ​

Input:

  • nums (List[int]): Array of non-negative integers
  • target (int): Target sum to achieve

Output:

  • (int): Number of ways to achieve the target

Constraints ​

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

Examples ​

Example 1: Multiple Ways ​

Input: nums = [1, 1, 1, 1, 1], target = 3

Output: 5

Explanation:

There are 5 ways to assign symbols to make sum = 3:
1. -1 + 1 + 1 + 1 + 1 = 3
2. +1 - 1 + 1 + 1 + 1 = 3
3. +1 + 1 - 1 + 1 + 1 = 3
4. +1 + 1 + 1 - 1 + 1 = 3
5. +1 + 1 + 1 + 1 - 1 = 3

Pattern: Choose exactly one number to be negative.
C(5,1) = 5 ways

Example 2: Single Way ​

Input: nums = [1], target = 1

Output: 1

Explanation: Only +1 = 1 works.

Example 3: No Way ​

Input: nums = [1, 2, 3], target = 7

Output: 0

Explanation: Maximum possible sum = 1 + 2 + 3 = 6 < 7, so impossible.

Example 4: With Zeros ​

Input: nums = [0, 0, 1], target = 1

Output: 4

Explanation:

+0 + 0 + 1 = 1
+0 - 0 + 1 = 1
-0 + 0 + 1 = 1
-0 - 0 + 1 = 1

Note: +0 and -0 are considered different expressions!

Visual Explanation ​

Target Sum Visualization

ASCII Art: Decision Tree ​

nums = [1, 1, 1], target = 1

                    Start (sum=0)
                    /           \
                 +1             -1
                /  \           /  \
              +1   -1        +1   -1
             / \   / \      / \   / \
           +1 -1 +1 -1    +1 -1 +1 -1

Final sums at leaves:
+1+1+1 = 3
+1+1-1 = 1  <-- target!
+1-1+1 = 1  <-- target!
+1-1-1 = -1
-1+1+1 = 1  <-- target!
-1+1-1 = -1
-1-1+1 = -1
-1-1-1 = -3

Count of sum=1: 3 ways

Mathematical Transformation ​

Key insight: Transform to subset sum problem!

Let P = sum of numbers with '+' sign
Let N = sum of numbers with '-' sign

We have:
P + N = total_sum
P - N = target

Adding these equations:
2P = total_sum + target
P = (total_sum + target) / 2

So the problem becomes:
"Count subsets with sum = (total_sum + target) / 2"

This is a subset count problem!

Solution Code ​

Approach 1: Top-Down (Memoization) ​

python
from typing import List
from functools import lru_cache

def findTargetSumWays(nums: List[int], target: int) -> int:
    """
    Count ways to reach target using memoization.

    At each position, try adding + or - sign.

    Args:
        nums: Array of non-negative integers
        target: Target sum

    Returns:
        Number of ways to reach target

    Time Complexity: O(n * sum)
    Space Complexity: O(n * sum)
    """
    n = len(nums)

    @lru_cache(maxsize=None)
    def dp(index: int, current_sum: int) -> int:
        """Count ways to reach target from index with current_sum."""
        if index == n:
            return 1 if current_sum == target else 0

        # Try + and - for current number
        add = dp(index + 1, current_sum + nums[index])
        subtract = dp(index + 1, current_sum - nums[index])

        return add + subtract

    return dp(0, 0)

Complexity: Time O(n * sum) · Space O(n * sum)

  • Time: Each unique (index, current_sum) pair is computed once. With n elements and sums ranging from -total to +total, we have O(n * 2*total) = O(n * sum) states.
  • Space: The memoization cache stores all computed states plus the recursion stack depth of n.

Approach 2: Bottom-Up (2D DP) ​

python
from typing import List

def findTargetSumWays(nums: List[int], target: int) -> int:
    """
    Count ways using bottom-up DP with offset for negative sums.

    Args:
        nums: Array of non-negative integers
        target: Target sum

    Returns:
        Number of ways to reach target

    Time Complexity: O(n * sum)
    Space Complexity: O(n * sum)
    """
    total = sum(nums)

    # Target out of range
    if abs(target) > total:
        return 0

    n = len(nums)
    # Offset to handle negative indices
    # Sum ranges from -total to +total
    offset = total

    # dp[i][j] = ways to reach sum (j - offset) using first i numbers
    dp = [[0] * (2 * total + 1) for _ in range(n + 1)]
    dp[0][offset] = 1  # Sum 0 with 0 numbers

    for i in range(n):
        for j in range(2 * total + 1):
            if dp[i][j] > 0:
                # Add nums[i]
                dp[i + 1][j + nums[i]] += dp[i][j]
                # Subtract nums[i]
                dp[i + 1][j - nums[i]] += dp[i][j]

    return dp[n][target + offset]

Complexity: Time O(n * sum) · Space O(n * sum)

  • Time: We iterate through each of the n elements and for each, visit all 2*total+1 possible sums.
  • Space: The 2D dp table has dimensions (n+1) x (2*total+1) to store counts for all (element index, sum) combinations.

Approach 3: Space-Optimized (1D) ​

python
from typing import List
from collections import defaultdict

def findTargetSumWays(nums: List[int], target: int) -> int:
    """
    Count ways with O(sum) space using a dictionary.

    Args:
        nums: Array of non-negative integers
        target: Target sum

    Returns:
        Number of ways to reach target

    Time Complexity: O(n * sum)
    Space Complexity: O(sum)
    """
    # Count of ways to reach each sum
    dp = defaultdict(int)
    dp[0] = 1

    for num in nums:
        next_dp = defaultdict(int)
        for current_sum, count in dp.items():
            next_dp[current_sum + num] += count
            next_dp[current_sum - num] += count
        dp = next_dp

    return dp[target]

Complexity: Time O(n * sum) · Space O(sum)

  • Time: Each element is processed once, and for each element we update all reachable sums in constant time per sum.
  • Space: Using a dictionary that stores only reachable sums instead of a full 2D array, reducing space to O(distinct sums).

Approach 4: Subset Sum Transformation (Optimal) ​

python
from typing import List

def findTargetSumWays(nums: List[int], target: int) -> int:
    """
    Transform to subset sum problem for better efficiency.

    P - N = target
    P + N = total
    2P = target + total
    P = (target + total) / 2

    Count subsets with sum P.

    Args:
        nums: Array of non-negative integers
        target: Target sum

    Returns:
        Number of ways to reach target

    Time Complexity: O(n * sum)
    Space Complexity: O(sum)
    """
    total = sum(nums)

    # Check if solution is possible
    if abs(target) > total:
        return 0
    if (target + total) % 2 != 0:
        return 0

    subset_sum = (target + total) // 2

    # Count subsets with sum = subset_sum
    dp = [0] * (subset_sum + 1)
    dp[0] = 1

    for num in nums:
        # Iterate in reverse for 0/1 counting
        for j in range(subset_sum, num - 1, -1):
            dp[j] += dp[j - num]

    return dp[subset_sum]
java
public int findTargetSumWays(int[] nums, int target) {
    int total = 0;
    for (int n : nums) total += n;

    // Check if solution is possible
    if (Math.abs(target) > total) return 0;
    if ((target + total) % 2 != 0) return 0;

    int subsetSum = (target + total) / 2;

    // Count subsets with sum = subsetSum
    int[] dp = new int[subsetSum + 1];
    dp[0] = 1;

    for (int num : nums) {
        // Iterate in reverse for 0/1 counting
        for (int j = subsetSum; j >= num; j--) {
            dp[j] += dp[j - num];
        }
    }

    return dp[subsetSum];
}

Complexity: Time O(n * sum) · Space O(sum)

  • Time: Outer loop runs n times, inner loop runs up to subset_sum iterations. The mathematical transformation reduces the problem space.
  • Space: Only a 1D dp array of size subset_sum+1 is needed since we iterate backwards to avoid double-counting.

Approach 5: With Expression Generation ​

python
from typing import List, Tuple

def findTargetSumWays_with_expressions(
    nums: List[int],
    target: int
) -> Tuple[int, List[str]]:
    """
    Find count and generate all valid expressions.

    Args:
        nums: Array of non-negative integers
        target: Target sum

    Returns:
        Tuple of (count, list of expressions)

    Time Complexity: O(2^n)
    Space Complexity: O(n * 2^n)
    """
    expressions = []

    def backtrack(index: int, current_sum: int, path: List[str]):
        if index == len(nums):
            if current_sum == target:
                expressions.append(''.join(path))
            return

        # Try +
        path.append(f"+{nums[index]}")
        backtrack(index + 1, current_sum + nums[index], path)
        path.pop()

        # Try -
        path.append(f"-{nums[index]}")
        backtrack(index + 1, current_sum - nums[index], path)
        path.pop()

    backtrack(0, 0, [])
    return len(expressions), expressions

Complexity: Time O(2^n) · Space O(n * 2^n)

  • Time: Every element has 2 choices (+/-), creating a complete binary tree with 2^n leaves that must all be explored.
  • Space: The recursion stack is O(n), but we store all valid expressions which can be up to 2^n paths of length n each.

Complexity Analysis ​

ApproachTime ComplexitySpace Complexity
Top-DownO(n * sum)O(n * sum)
Bottom-Up 2DO(n * sum)O(n * sum)
Space-OptimizedO(n * sum)O(sum)
Subset Sum TransformO(n * sum)O(sum)

Where sum = sum of all elements in nums.

Edge Cases ​

  1. Target out of range:

    python
    findTargetSumWays([1, 2], 10)  # Returns 0
    findTargetSumWays([1, 2], -10)  # Returns 0
  2. Single element:

    python
    findTargetSumWays([5], 5)   # Returns 1 (+5)
    findTargetSumWays([5], -5)  # Returns 1 (-5)
    findTargetSumWays([5], 0)   # Returns 0
  3. All zeros:

    python
    findTargetSumWays([0, 0, 0], 0)  # Returns 8 (2^3)
  4. Target is zero:

    python
    findTargetSumWays([1, 1], 0)  # Returns 2 (+1-1 or -1+1)
  5. Impossible parity:

    python
    # target + total must be even for subset sum approach
    findTargetSumWays([1], 2)  # Returns 0 (1+2=3, odd)

Why Subset Sum Works ​

Original problem:
Assign + or - to each number to get target.

Let P = sum of positive numbers
Let N = sum of negative numbers

Equations:
P + N = total_sum  (all numbers used)
P - N = target     (desired result)

Solving:
2P = total_sum + target
P = (total_sum + target) / 2

So we need to find subsets with sum P.
The remaining numbers (with - sign) will automatically
sum to N = total_sum - P.

And P - N = P - (total_sum - P) = 2P - total_sum = target

This transforms O(n * 2^n) brute force into O(n * sum) DP!

Key Insights ​

  1. Binary Choice: Each number gets either '+' or '-', creating 2^n possibilities in brute force.

  2. Subset Sum Reduction: The mathematical transformation reduces this to a subset sum counting problem.

  3. Parity Check: If (target + total) is odd, no solution exists.

  4. Range Check: If |target| > total, no solution exists.

  5. Zeros: Each zero doubles the count of solutions since +0 = -0 = 0.

Partition Equal Subset Sum (LeetCode 416)

Problem: Check if array can be partitioned into two equal-sum subsets.

Key Insight: Special case of Target Sum where target = 0. When P - N = 0, we need P = N = sum/2.

Approach: Same subset sum DP but boolean instead of counting: dp[s] = dp[s] or dp[s-num]. Just check reachability.

Complexity: O(n * sum) time, O(sum) space

Coin Change 2 (LeetCode 518)

Problem: Count number of combinations to make up amount using coins (can reuse coins).

Key Insight: Unlike Target Sum (0/1), coins can be reused. Iterate coins in outer loop to count combinations, not permutations.

Approach: dp[a] += dp[a-coin] with outer loop on coins. Forward iteration allows reuse. Outer coin loop ensures combinations, not permutations.

Complexity: O(n * amount) time, O(amount) space

Expression Add Operators (LeetCode 282)

Problem: Add +, -, * between digits to achieve target. Return all valid expressions.

Key Insight: More complex than Target Sum - must handle multiplication precedence and multi-digit numbers.

Approach: Backtrack with params (index, current_value, prev_operand). For *, adjust: new_val = val - prev + prev * cur. Generate multi-digit numbers avoiding leading zeros.

Complexity: O(4^n * n) time, O(n) space for recursion

Last Stone Weight II (LeetCode 1049)

Problem: Smash stones, remaining weight = |x - y|. Find minimum possible weight of last stone.

Key Insight: Equivalent to partitioning stones into two groups and minimizing their difference. Same as minimum subset sum difference.

Approach: Find subset sum closest to total/2. Min remaining = total - 2 * max_achievable_sum. Same as Target Sum with target = 0.

Complexity: O(n * sum) time, O(sum) space