diff --git a/combination-sum.py b/combination-sum.py new file mode 100644 index 00000000..6d676592 --- /dev/null +++ b/combination-sum.py @@ -0,0 +1,89 @@ +# Time Complexity: +# O(n * 2^(m+n)) where m is the candidates length and n is the target + +# Space Complexity: +# O(n * h) -> O(n^2) +class Solution: + def combinationSum(self, candidates, target): + self.result = [] + self.helper(candidates,target,0,[]) + return self.result + + def helper(self, candidates, target, i,path): + + # base + if target < 0 or i == len(candidates): + return + + if target == 0: + self.result.append(path) + return + + # no choose + self.helper(candidates,target,i+1,list(path)) # create new deep copies otherwise the recursive calls will all reference the same path + + path.append(candidates[i]) + + # choose + self.helper(candidates,target-candidates[i],i,list(path)) + +# Time Complexity: +# O(n * 2^(m+n)) where m is the candidates length and n is the target + +# Space Complexity: +# O(n * h) -> O(n^2) + +# the first solution copies all lists - creating snapshots at each fork in the tree. With 1,000 forks, you create 1,000 lists +# In this solution, you have 1 list and just add/remove from it 1,000 times +class Solution: + def combinationSum(self, candidates, target): + self.result = [] + self.helper(candidates,target,0,[]) + return self.result + + + def helper(self,candidates,target,i,path): + if target < 0 or i == len(candidates): + return + + if target == 0: + self.result.append(list(path)) + + self.helper(candidates,target,i+1,path) + path.append(candidates[i]) + self.helper(candidates,target-candidates[i],i,path) + path.pop() + +# Time Complexity: +# O(n*2^(m+n)) +# conversion to list is the n (copy). Otherwise at each branch you have 2 decisions, so total nodes is 2^depth +# At each recursive step you do 1 of 2 things: +# increment i by moving to next candidate (M candidates). +# reduce target (stay at same index but reduce target) +# So, M (exhausting candidates) + T (exhausting target) +# Space Complexity: +# O(n) + +class Solution: + def combinationSum(self, candidates, target): + self.result = [] + self.helper(candidates,target,i,path) + return self.result + + def helper(self,candidates, target, pivot, path): + if target < 0 or pivot == len(candidates): + return + if target == 0: + self.result.append(list(path)) + return + + for i in range(pivot,len(candidates)): + path.append(candidates[i]) + self.helper(candidates,target-candidates[i],i,path) + path.pop() + + + + + + diff --git a/operations-expressions.py b/operations-expressions.py new file mode 100644 index 00000000..b96bd11c --- /dev/null +++ b/operations-expressions.py @@ -0,0 +1,35 @@ +# time: O(4^n) +# Have 4 options at each recursive step: +# 1) extend current number +# 2) add + +# 3) add - +# 4) add * +# space: O(n) + +class Solution: + def addOperators(self, num: str, target: int) -> List[str]: + self.result = [] + + def helper(num,target,pivot,calc,tail,path): + if pivot == len(num) + if calc == target: + self.result.append(path) + + for i in range(pivot,len(num)): + # ignore leading 0's - 05 does not become 5 + if num[pivot]=='0' and i != pivot: + break + curr = int(num[pivot:i+1]) + + if pivot == 0: + helper(num, target, i+1, curr, curr, path + str(curr)) + else: + # + + helper(num, target, i+1, calc + curr, curr, path + "+" + str(curr)) + # - + helper(num, target, i+1, calc - curr, -curr, path + "-" + str(curr)) + # * + helper(num, target, i+1, calc - tail + (tail * curr), tail * curr, path + "*" + str(curr)) + + helper(num, target, 0, 0, 0, "") + return self.result \ No newline at end of file