Write a function fib that takes in a number argument, n, and returns the n-th number of the Fibonacci sequence.
The 0-th number of the sequence is 0.
The 1-st number of the sequence is 1.
To generate further numbers of the sequence, calculate the sum of previous two numbers.
Solve this recursively. Also try with memoization.
def fib(n):
if n == 0:
return 0
elif n == 1:
return 1
return fib(n - 1) + fib(n - 2)
O(2^n) / O(n)
^ Know how to explain and visualize the complexity using trees and call stack.
How to break down and visualize recursive problems?
Use a tree-like structure with nodes.
Fib(6) depends on fib(5) and fib(4)
6 5 4
How does the call stack work? (if you visualize as tree)
It’s DFS (L, R, root) postorder?
You are climbing a staircase. It takes n steps to reach the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.
Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
You may assume that you have an infinite number of each kind of coin.
minVal = math.inf
for coin in coins:
minVal = min(minVal, 1 + self._coinChange(coins, amount - coin, hash))
hash[amount] = minVal
return minVal
O(n * m) / O(n)
Can you hash a tuple?
YES!
There is a robot on an m x n grid. The robot is initially located at the top-left corner (i.e., grid[0][0]). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]). The robot can only move either down or right at any point in time.
Given the two integers m and n, return the number of possible unique paths that the robot can take to reach the bottom-right corner.
The test cases are generated so that the answer will be less than or equal to 2 * 109.
O(n * m) / O(m * n)
(you can also try the clever Neetcode solution)
https://www.youtube.com/watch?v=IlEsdxuD4lY&t=641s
How do you calculate time complexity of dynamic programming problem?
FUNDAMENTAL DP PROBLEM - You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police.
(Also consider the iterative Neetcode solution later)
class Solution:
def rob(self, nums: List[int]) -> int:
return self._rob(nums, {}, 0)
def _rob(self, nums, hash, start):
if start >= len(nums):
return 0
if start in hash:
return hash[start]
include = nums[start] + self._rob(nums, hash, start + 2)
exclude = self._rob(nums, hash, start + 1)
result = max(include, exclude)
hash[start] = result
return resultBottom up DP solution:
class Solution:
def rob(self, nums: List[int]) -> int:
rob1, rob2 = 0, 0
for n in nums:
temp = max(rob1 + n, rob2)
rob1 = rob2
rob2 = temp
return rob2
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed. All houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, adjacent houses have a security system connected, and it will automatically contact the police if two adjacent houses were broken into on the same night.
Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police.
Given a string s, return the longest
palindromic
substring
in s.
Example 1:
Input: s = “babad”
Output: “bab”
Explanation: “aba” is also a valid answer.
Example 2:
Input: s = “cbbd”
Output: “bb”
class Solution:
def longestPalindrome(self, s: str) -> str:
res = ‘’
for i in range(len(s)):
l, r = i, i
res = max(res, self.expand(i, i, s), self.expand(i, i + 1, s), key=len)
return res
def expand(self, l, r, s):
while l >= 0 and r < len(s) and s[l] == s[r]:
l -= 1
r += 1
return s[l + 1:r]O(n^2) / O(n)
Given a string s, return the number of palindromic substrings in it.
A string is a palindrome when it reads the same backward as forward.
A substring is a contiguous sequence of characters within the string.
Example 1:
Input: s = “abc”
Output: 3
Explanation: Three palindromic strings: “a”, “b”, “c”.
Example 2:
Input: s = “aaa”
Output: 6
Explanation: Six palindromic strings: “a”, “a”, “a”, “aa”, “aa”, “aaa”.
class Solution:
def countSubstrings(self, s: str) -> int:
count = 0
for i in range(len(s)):
count += self._countSubstrings(i, i, s)
count += self._countSubstrings(i, i + 1, s)
return count
def _countSubstrings(self, l, r, s):
count = 0
while l >= 0 and r < len(s) and s[l] == s[r]:
count += 1
l -= 1
r += 1
return count
A message containing letters from A-Z can be encoded into numbers using the following mapping:
‘A’ -> “1”
‘B’ -> “2”
…
‘Z’ -> “26”
To decode an encoded message, all the digits must be grouped then mapped back into letters using the reverse of the mapping above (there may be multiple ways). For example, “11106” can be mapped into:
“AAJF” with the grouping (1 1 10 6)
“KJF” with the grouping (11 10 6)
Note that the grouping (1 11 06) is invalid because “06” cannot be mapped into ‘F’ since “6” is different from “06”.
Given a string s containing only digits, return the number of ways to decode it.
The test cases are generated so that the answer fits in a 32-bit integer.
Example 1:
class Solution:
def numDecodings(self, s: str) -> int:
return self._numDecodings(s, 0, {})
def _numDecodings(self, s, i, memo):
if i in memo:
return memo[i]
if i == len(s):
return 1
if s[i] == '0':
return 0
result = self._numDecodings(s, i + 1, memo)
if i + 1 < len(s) and (10 <= int(s[i:i + 2]) <= 26):
result += self._numDecodings(s, i + 2, memo)
memo[i] = result
return resultGiven an integer array nums, find a
subarray
that has the largest product, and return the product.
The test cases are generated so that the answer will fit in a 32-bit integer.
Example 1:
Input: nums = [2,3,-2,4]
Output: 6
Explanation: [2,3] has the largest product 6.
Not recursive DP.
- You want to track the maximum and minimum at any given point starting from the beginning.
- minimum, maximum, result
- loop through and calculate min and max of (min * num, max * num, num)
- This works because whenever you get a minimum number, it’s going to flip a the minimum to the maximum and vice versa.
Given an integer array nums, return the length of the longest strictly increasing
subsequence
.
Example 1:
Input: nums = [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.
O(n^2)
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
maxList = [1 for i in range(len(nums))]
for i in range(len(nums) - 1, -1, -1):
for j in range(i + 1, len(nums)):
if nums[j] > nums[i]:
maxList[i] = max(maxList[i], maxList[j] + 1)
return max(maxList)What’s the 3-step approach you can use to think about DP problems?
Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated sequence of one or more dictionary words.
Note that the same word in the dictionary may be reused multiple times in the segmentation.
Example 1:
Input: s = “leetcode”, wordDict = [“leet”,”code”]
Output: true
Explanation: Return true because “leetcode” can be segmented as “leet code”.
class Solution:
def wordBreak(self, s: str, wordDict: List[str]) -> bool:
breakList = [False for i in range(len(s) + 1)]
breakList[-1] = True
for i in range(len(s) - 1, -1, -1):
for w in wordDict:
if s[i : i + len(w)] == w and breakList[i + len(w)]:
breakList[i] = True
return breakList[0]Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order.
The same number may be chosen from candidates an unlimited number of times. Two combinations are unique if the
frequency
of at least one of the chosen numbers is different.
The test cases are generated such that the number of unique combinations that sum up to target is less than 150 combinations for the given input.
class Solution:
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
res = []
def dfs(i, curList, total):
if total == target:
res.append(curList)
return
if total > target or i >= len(candidates):
return
dfs(i, curList + [candidates[i]], total + candidates[i])
dfs(i + 1, curList, total)
dfs(0, [], 0)
return resGiven an m x n grid of characters board and a string word, return true if word exists in the grid.
The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once.
O(n * m * 4^(len(word))) - it’s about the best you can do with this problem.
Given an m x n grid of characters board and a string word, return true if word exists in the grid.
The word can be constructed from letters of sequentially adjacent cells, where adjacent cells are horizontally or vertically neighboring. The same letter cell may not be used more than once.
O(n * m * 4^(len(word))) - it’s about the best you can do with this problem.
Given an integer array nums, find the
subarray with the largest sum, and return its sum.
(subarray means it’s contiguous)
You are given an integer array nums. You are initially positioned at the array’s first index, and each element in the array represents your maximum jump length at that position.
Return true if you can reach the last index, or false otherwise.
O(n)
def canJump(self, nums: List[int]) -> bool:
goal = len(nums) - 1
for i in range(len(nums) - 2, -1, -1):
if goal - i <= nums[i]:
goal = i
return goal == 0