dp row i = numbers already signed · column = running total · cell = ways to reach target from there
Call stack bottom → top
Map<String, Integer> dp = new HashMap<>(); // "i,total" -> waysint[] nums;int target;public int findTargetSumWays(int[] nums, int target) { this.nums = nums; this.target = target; return backtrack(0, 0);}private int backtrack(int i, int total) { if (i == nums.length) { return total == target ? 1 : 0; } String key = i + "," + total; if (dp.containsKey(key)) { return dp.get(key); } int ways = backtrack(i + 1, total + nums[i]) + backtrack(i + 1, total - nums[i]); dp.put(key, ways); return ways;}
- i, total
- —
- nums[i]
- —
- memo entries
- 0
- memo hits
- 0
Why it works
- Brute force tries every sign pattern: 2n of them, each summed in O(n).
- The future only depends on (i, total). However you signed the first
inumbers, if they add up to the sametotal, the ways to finish are the same. So each pair is solved once and cached, and later calls are memo hits. - Complexity:
itakes n+1 values andtotalstays within ±S, so there are at most (n+1)·(2S+1) states, each doing O(1) work: O(n·S) time and space. - Pitfall: totals go negative, so an array index needs an offset of S; a
HashMapkeyed by"i,total"avoids that. And zeros double the count:+0and−0are different sign choices. - Follow-up: if P is the sum of the plus group, then P − (S − P) = target, so P = (S + target) / 2. The problem becomes "count subsets that add up to P", a 1-D 0/1 knapsack in O(n·S) time and O(S) space.