Target Sum
Assign sign to each number + or - to form target sum
Target Sum
Problem Statement
You are given an integer array, nums, and an integer, target.
Your goal is to build a mathematical expression using all the numbers in the array. For every number, you must place either a + (plus) or a - (minus) sign right before it.
After assigning the signs and evaluating the expression, the final sum must equal the target.
Return the total number of different expressions you can build that evaluate to the target.
Examples
Example 1
- Input: nums = [1, 1, 1, 1, 1], target = 3
- Output: 5
- Explanation: There are 5 ways to assign symbols to make the sum equal to 3:
- -1 + 1 + 1 + 1 + 1 = 3
- +1 - 1 + 1 + 1 + 1 = 3
- +1 + 1 - 1 + 1 + 1 = 3
- +1 + 1 + 1 - 1 + 1 = 3
- +1 + 1 + 1 + 1 - 1 = 3
Example 2
- Input: nums = [1], target = 1
- Output: 1
The Core Idea: Reducing to Subset Sum
At first glance, this looks like a new problem. However, it is mathematically identical to the "Count Partitions with Given Difference" problem.
Think of the array divided into two groups:
- Group S1: All numbers with a
+sign. - Group S2: All numbers with a
-sign.
We know two things must be true:
S1 - S2 = target(This is the requirement of the problem).S1 + S2 = totalSum(The sum of all numbers in both groups equals the total sum of the array).
If we subtract the first equation from the second, we get:
2 * S2 = totalSum - target
S2 = (totalSum - target) / 2
This reveals a massive shortcut: we don't need to track + and - signs at all. We just need to find how many subsets (S2) in the array add up exactly to (totalSum - target) / 2.
Algorithm
- Calculate the
totalSumof the array. - Check for impossible edge cases:
- If
totalSum - target < 0, the target is too large to reach. Return0. - If
(totalSum - target)is an odd number, we can't divide it evenly into two integer partitions. Return0.
- If
- Calculate our new target subset sum:
(totalSum - target) / 2. - Use a standard Top-Down DP (Memoization) approach to count how many subsets equal this new target.
- Handle the special base case for zeros: If the remaining target is
0and the current number is0, return2. This is because we can either "pick" the0or "skip" the0, and both choices keep our sum at0.
Code Implementation
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var findTargetSumWays = function (arr, target) {
function countPartitions(arr, d) {
const totalSum = arr.reduce((a, b) => a + b, 0);
// Edge cases: The difference cannot be negative, and must be even
if (totalSum - d < 0 || (totalSum - d) % 2 !== 0) return 0;
// This is the subset sum we need to find
const subsetTarget = (totalSum - d) / 2;
const n = arr.length;
// Initialize DP table with -1
const dp = Array.from({ length: n }, () => Array(subsetTarget + 1).fill(-1));
const MOD = 1e9 + 7;
function solve(idx, currentTarget) {
// Base cases
if (idx === 0) {
// If target is 0 and element is 0, we have 2 choices (pick or skip)
if (currentTarget === 0 && arr[0] === 0) return 2;
// If target is 0 (and element is not 0), or element equals target, 1 way
if (currentTarget === 0 || currentTarget === arr[0]) return 1;
// Otherwise, 0 ways
return 0;
}
if (dp[idx][currentTarget] !== -1) return dp[idx][currentTarget];
let notTake = solve(idx - 1, currentTarget);
let take = 0;
if (arr[idx] <= currentTarget) {
take = solve(idx - 1, currentTarget - arr[idx]);
}
return (dp[idx][currentTarget] = (take + notTake) % MOD);
}
return solve(n - 1, subsetTarget);
}
return countPartitions(arr, target);
};