The problem. Given an integer array nums and an integer target, put a + or a - in front of every number. Count how many of these expressions add up to target.
nums = [1, 1, 1, 1, 1], target = 3 -> 5
nums = [1], target = 1 -> 1
nums = [1, 2, 3], target = 7 -> 0 (the most you can reach is 6)
nums = [0, 1], target = 1 -> 2 (+0 +1 and -0 +1 both count)Brute force tries both signs for each number: 2ⁿ expressions. Better: let P be the sum of the numbers that get + and N the sum of those that get -. Then P - N = target and P + N = total. Adding the two equations gives `P = (total + target) / 2`. So every valid expression is just a subset that sums to P — the same 0/1 knapsack as Partition Equal Subset Sum, except now we count subsets instead of asking whether one exists. If total + target is odd or |target| > total, no expression works.
function findTargetSumWays(nums, target) { const count = (i, sum) => { // ways to finish, given the sum so far if (i === nums.length) return sum === target ? 1 : 0; return count(i + 1, sum + nums[i]) + count(i + 1, sum - nums[i]); // + or - }; return count(0, 0);}Time O(2ⁿ), space O(n). Fine for LeetCode's n ≤ 20 (about a million calls), but it repeats work: many different sign choices reach the same (i, sum).
function findTargetSumWays(nums, target) { const memo = new Map(); // "i,sum" -> ways const count = (i, sum) => { if (i === nums.length) return sum === target ? 1 : 0; const key = i + "," + sum; if (!memo.has(key)) memo.set(key, count(i + 1, sum + nums[i]) + count(i + 1, sum - nums[i])); return memo.get(key); }; return count(0, 0);}Time O(n · total), space O(n · total). The running sum can be anything from -total to total, so there are n × (2 · total + 1) states. Java and C++ shift the sum by total to use it as an array index.
Turn the signs into a subset sum, then count the subsets one number at a time.
function findTargetSumWays(nums, target) { const total = nums.reduce((a, b) => a + b, 0); if (Math.abs(target) > total || (total + target) % 2 !== 0) return 0; // no split works const plus = (total + target) / 2; // the + numbers must sum to this const ways = new Array(plus + 1).fill(0); // ways[s]: subsets so far that sum to s ways[0] = 1; // the empty subset for (const x of nums) { for (let s = plus; s >= x; s--) ways[s] += ways[s - x]; // backwards: x is used once } return ways[plus];}Time O(n · P), space O(P). Zeros need no special case: for x = 0 the loop runs ways[s] += ways[s], doubling every count — exactly right, since +0 and -0 are two different expressions.
|target| > total: 0.(total + target) / 2.target = 0.|P - N|.