Skip to content

Update 0494-target-sum.py (runtime and memory > 80%) #2296

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions python/0494-target-sum.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
class Solution:
def findTargetSumWays(self, nums: List[int], target: int) -> int:
dp = {} # (index, total) -> # of ways

def backtrack(i, total):
if i == len(nums):
return 1 if total == target else 0
if (i, total) in dp:
return dp[(i, total)]
"""
Hello... first time contributor. Just used your resource, and found it super helpful.
I noticed you have habit of running DP problem from backward (e.g. for i in range(n-1, -1, -1, instead of for in in range(n),
it makes my learning take extra effort, because going forward is more natural for me haha. (the code below is going forward direction)

dp[(i, total)] = backtrack(i + 1, total + nums[i]) + backtrack(
i + 1, total - nums[i]
)
return dp[(i, total)]
Anyway, I found that your solution in this problem surprisingly is not as optimized as usual.
Let's define dp[i,a] as number of combinations that uses coins[:i] to sum to a, our goal thus is dp[len(nums), target]
Two common pattern that you taught before to optimize:
1. just use previous row and current row in dp table, since dp[i,a] always depends on dp[i-1, a+n] and dp[i-1, a-n].
As implemented below as dp and next_dp
2. if you notice the solution that build from base case (i = 0, or coins[:0] = [] empty array), most of the elements in dp are pretty sparse.
Thats why we use dict in dp instead of big size array. And also the next_dp will only branch out from non zero previous dp,
thus we should just need to iterate over previous dp elements instead of the entire range of values.

return backtrack(0, 0)
The solution below got >80% in runtime and >80% in memory, all using your earlier DP optimization principles!
"""
def findTargetSumWays(self, nums: List[int], target: int) -> int:
dp = {0: 1}
for n in nums:
next_dp = {}
for a in dp:
next_dp[a - n] = next_dp.get(a - n, 0) + dp[a]
next_dp[a + n] = next_dp.get(a + n, 0) + dp[a]
dp = next_dp
return dp.get(target, 0)