This will serve as a quick revision and repo of all algorithms which I intend to master.
Write a program to implement
First is a simple iterative approach.
def pow(n,k):
res=1
for _ in range(k):
res*=n
return res
assert pow(2,5)==32
The key is to realize that
We divide the exponent (
def pow(n,k):
if k==1: return n
return pow(n,k//2) * pow(n, k//2) if k%2==0 else n * pow(n, k-1)
assert pow(2,5)==2**5
assert pow(3,5)==3**5
Any recursive function can be represented as an iterative one with a stack data structure. Which is what is happening in the recursive one.
def pow(n,k):
op_stack=[]
res=n
# Create the stack
while k>1:
if k%2==0:
op_stack.append('square')
k=k//2
else:
op_stack.append('multiply')
k-=1
# Unravel the stack
while op_stack:
op=op_stack.pop()
if op=='square':
res=res*res
else:
res*=n
return res
assert pow(2,10)==2**10
def can_sum(target,nums):
if target==0: return True
if target<0: return False
for num in nums:
if can_sum(target-num, nums):
return True
return False
Given a target and list of numbers write a function which determines whether the target can be obtained by summing numbers from the list.
assert can_sum(7, (2,3))
assert not can_sum(13, [2,4,6])
The trick is to return immediately as soon as target becomes 0. This
return passes all the way to the top call and function returns True
.
However if after looping over all the numbers in the list, no solution
is found, we return False
.
The time complexity of can_sum
is n
is the target and
m
is the length of nums
. The following example highlights this.
# can_sum(250, [7,14])
def can_sum(target,nums,memo={}):
if target in memo: return memo[target]
if target==0: return True
if target<0: return False
for num in nums:
if can_sum(target-num,nums,memo):
memo[target]=True
return True
memo[target]=False
return False
Memoization makes the call to can_sum(250, [7,14])
which previously
took way too long super fast.
can_sum(250, [7,14])
False
Given a target and list of numbers return a subset which sums to the target. All numbers are positive and a give number from the list can be used multiple times.
If the numbers cannot be summed to target, return None.
def how_sum_memo(target,nums,memo={}):
if target in memo: return memo[target]
if target==0: return []
if target<0: return None
for num in nums:
res=how_sum_memo(target-num, nums, memo)
if isinstance(res,list):
res.append(num)
memo[target]=res
return res
memo[target]=None
return None
how_sum_memo(200, [7,14])
CPU times: user 39 µs, sys: 4 µs, total: 43 µs
Wall time: 47.9 µs
Given a target and a list of numbers, return a list of all subsets which sum to this target. Multiple usage of a number is permitted.
def all_sums(target,nums):
if target==0: return [[]]
if target<0: return []
sol=[]
for num in nums:
res=all_sums(target-num, nums)
if len(res)>=1:
for l in res:
l.append(num)
sol+=res
return sol
all_sums(5, [2,3])
[[3, 2], [2, 3]]
Given a target and a list of numbers, return the subset that sums to target and has the least number of elements in it.
def best_sum(target,nums):
if target==0: return []
if target<0: return None
sol=None
for num in nums:
res=best_sum(target-num,nums)
if isinstance(res,list):
res.append(num)
if (sol is None) or (len(res) < len(sol)):
sol=res
return sol
best_sum(7,[1,3,7])
[7]
Write a function canConstruct(target, wordBank)
that accepts target
string and an array of strings. The function should return a boolean
indicating whether or not the target
can be constructed by
concatenating elements of the wordBank array. You may reuse elements of
wordBank as many times as needed.
def can_construct(target, words):
if target=='': return True
for word in words:
if target.startswith(word):
return can_construct(target[len(word):], words)
return False
can_construct('abcd', ['ab', 'bc', 'cd'])
True
can_construct("", ["f"])
True
can_construct('ffffffffffffffffffffffd', ['f','fff','fffff', 'd'])
True
Write a function countConstruct(target, wordBank)
that accepts a
target string and an array of strings. The function should return the
number of ways that the target can be constructed by concatenating
elements of the wordBank array.
You may reuse elements of wordBank as many times as needed.
def count_construct(target, words):
if target=='': return 1
res=0
for word in words:
if target.startswith(word):
res += count_construct(target[len(word):], words)
return res
count_construct('abcdef', ['ab', 'abc', 'cd', 'def', 'abcd'])
1
count_construct('purple', ['purp', 'p', 'ur', 'le', 'purpl'])
2
Given a target string and list of substrings, return all the set of substrings which can be concatenated to create the target.
Multiple usage of substrings is permitted.
def all_construct(target, subs):
if target=='': return [[]]
sol=[]
for sub in subs:
if target.startswith(sub):
res=all_construct(target[len(sub):], subs)
if len(res)>=1:
for l in res:
l.append(sub)
sol+=res
return sol
all_construct('abc',['a','bc', 'ab', 'c'])
[['bc', 'a'], ['c', 'ab']]