Google Dynamic Programming Leetcode Study Guide
42. Trapping Rain Water
Problem Description
Given n non-negative integers a1, a2, …, an , where each integer represents the height of a vertical line on a chart, find two lines, which together with the x-axis forms a container, such that area between them is the maximum.
Solution
def trap(height: list[int]) -> int:
"""
Returns the amount of rain water that can be trapped.
Args:
height (list[int]): A list of non-negative integers representing the height of each bar.
Returns:
int: The amount of rain water that can be trapped.
"""
if not height:
return 0
# Initialize two pointers
left, right = 0, len(height) - 1
max_left, max_right = height[left], height[right]
result = 0
while left < right:
max_left = max(max_left, height[left])
max_right = max(max_right, height[right])
if max_left < max_right:
result += max_left - height[left]
left += 1
else:
result += max_right - height[right]
right -= 1
return result
Time and Space Complexity
- Time complexity: O(n), where n is the number of bars.
- Space complexity: O(1), as we only use a constant amount of space.
Sample Input and Output
- Input:
height = [0,1,0,2,1,0,1,3,2,1,2,1]
- Output:
6
1547. Minimum Cost to Cut a Stick
Problem Description
Given a stick of length n, and a list of cuts. A cut is represented by an integer in the list, and the cost of a cut is the product of the current length of the stick and the cut. Find the minimum cost to make all the cuts.
Solution
def minCost(n: int, cuts: list[int]) -> int:
"""
Returns the minimum cost to make all the cuts.
Args:
n (int): The length of the stick.
cuts (list[int]): A list of cuts.
Returns:
int: The minimum cost to make all the cuts.
"""
cuts.sort()
cuts = [0] + cuts + [n]
dp = [[0] * len(cuts) for _ in range(len(cuts))]
for length in range(2, len(cuts)):
for left in range(len(cuts) - length):
right = left + length
dp[left][right] = float('inf')
for i in range(left + 1, right):
dp[left][right] = min(dp[left][right], dp[left][i] + dp[i][right] + (cuts[right] - cuts[left]) * (cuts[i] - cuts[left]))
return dp[0][len(cuts) - 1]
Time and Space Complexity
- Time complexity: O(n^3), where n is the number of cuts.
- Space complexity: O(n^2), as we use a 2D DP array.
Sample Input and Output
- Input:
n = 7, cuts = [1,3,4,5]
- Output:
16
10. Regular Expression Matching
Problem Description
Given an input string s
and a pattern p
, implement regular expression matching with support for ‘.’ and ‘*’.
Solution
def isMatch(s: str, p: str) -> bool:
"""
Returns whether the input string matches the pattern.
Args:
s (str): The input string.
p (str): The pattern.
Returns:
bool: Whether the input string matches the pattern.
"""
dp = [[False] * (len(p) + 1) for _ in range(len(s) + 1)]
dp[-1][-1] = True
for i in range(len(s), -1, -1):
for j in range(len(p) - 1, -1, -1):
match = i < len(s) and (p[j] == s[i] or p[j] == '.')
if j + 1 < len(p) and p[j + 1] == '*':
dp[i][j] = dp[i][j + 2] or match and dp[i + 1][j]
else:
dp[i][j] = match and dp[i + 1][j + 1]
return dp[0][0]
Time and Space Complexity
- Time complexity: O(len(s) * len(p)), where len(s) and len(p) are the lengths of the input string and pattern respectively.
- Space complexity: O(len(s) * len(p)), as we use a 2D DP array.
Sample Input and Output
- Input:
s = "aa", p = "a"
- Output:
False
44. Wildcard Matching
Problem Description
Given an input string s
and a pattern p
, implement wildcard matching with support for ‘?’ and ‘*’.
Solution
def isMatch(s: str, p: str) -> bool:
"""
Returns whether the input string matches the pattern.
Args:
s (str): The input string.
p (str): The pattern.
Returns:
bool: Whether the input string matches the pattern.
"""
dp = [[False] * (len(p) + 1) for _ in range(len(s) + 1)]
dp[0][0] = True
for j in range(1, len(p) + 1):
if p[j - 1] == '*':
dp[0][j] = dp[0][j - 1]
for i in range(1, len(s) + 1):
for j in range(1, len(p) + 1):
match = p[j - 1] == s[i - 1] or p[j - 1] == '?'
if p[j - 1] == '*':
dp[i][j] = dp[i][j - 1] or match and dp[i - 1][j]
else:
dp[i][j] = match and dp[i - 1][j - 1]
return dp[len(s)][len(p)]
Time and Space Complexity
- Time complexity: O(len(s) * len(p)), where len(s) and len(p) are the lengths of the input string and pattern respectively.
- Space complexity: O(len(s) * len(p)), as we use a 2D DP array.
Sample Input and Output
- Input:
s = "aa", p = "*"
- Output:
True
55. Jump Game
Problem Description
Given an array of non-negative integers nums
, you are initially placed at the first index of the array. Each element in the array represents your maximum jump length from that position. Determine if you are able to reach the last index.
Solution
def canJump(nums: list[int]) -> bool:
"""
Returns whether we can reach the last index.
Args:
nums (list[int]): A list of non-negative integers.
Returns:
bool: Whether we can reach the last index.
"""
last_pos = len(nums) - 1
for i in range(len(nums) - 1, -1, -1):
if i + nums[i] >= last_pos:
last_pos = i
return last_pos == 0
Time and Space Complexity
- Time complexity: O(n), where n is the length of the input array.
- Space complexity: O(1), as we only use a constant amount of space.
Sample Input and Output
- Input:
nums = [2,3,1,1,4]
- Output:
True
63. Unique Paths II
Problem Description
A robot is located at the top-left corner of a m x n grid. The robot can only move either down or right at any point in time. The grid has obstacles. How many possible unique paths are there?
Solution
def uniquePathsWithObstacles(obstacleGrid: list[list[int]]) -> int:
"""
Returns the number of unique paths.
Args:
obstacleGrid (list[list[int]]): A 2D list representing the grid.
Returns:
int: The number of unique paths.
"""
m, n = len(obstacleGrid), len(obstacleGrid[0])
dp = [[0] * n for _ in range(m)]
for i in range(m):
for j in range(n):
if obstacleGrid[i][j] == 0:
if i == 0 and j == 0:
dp[i][j] = 1
elif i == 0:
dp[i][j] = dp[i][j - 1]
elif j == 0:
dp[i][j] = dp[i - 1][j]
else:
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
return dp[-1][-1]
Time and Space Complexity
- Time complexity: O(m * n), where m and n are the dimensions of the grid.
- Space complexity: O(m * n), as we use a 2D DP array.
Sample Input and Output
- Input:
obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
- Output:
2
64. Minimum Path Sum
Problem Description
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right, which minimizes the sum of all numbers along its path.
Solution
def minPathSum(grid: list[list[int]]) -> int:
"""
Returns the minimum path sum.
Args:
grid (list[list[int]]): A 2D list representing the grid.
Returns:
int: The minimum path sum.
"""
m, n = len(grid), len(grid[0])
for i in range(1, n):
grid[0][i] += grid[0][i - 1]
for i in range(1, m):
grid[i][0] += grid[i - 1][0]
for i in range(1, m):
for j in range(1, n):
grid[i][j] += min(grid[i - 1][j], grid[i][j - 1])
return grid[-1][-1]
Time and Space Complexity
- Time complexity: O(m * n), where m and n are the dimensions of the grid.
- Space complexity: O(1), as we modify the input grid.
Sample Input and Output
- Input:
grid = [[1,3,1],[1,5,1],[4,2,1]]
- Output:
7
72. Edit Distance
Problem Description
Given two words word1
and word2
, find the minimum number of operations (insertions, deletions and substitutions) required to convert word1
to word2
.
Solution
def minDistance(word1: str, word2: str) -> int:
"""
Returns the minimum edit distance.
Args:
word1 (str): The first word.
word2 (str): The second word.
Returns:
int: The minimum edit distance.
"""
m, n = len(word1), len(word2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m + 1):
dp[i][0] = i
for j in range(n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
return dp[m][n]
Time and Space Complexity
- Time complexity: O(m * n), where m and n are the lengths of the two words.
- Space complexity: O(m * n), as we use a 2D DP array.
Sample Input and Output
- Input:
word1 = "horse", word2 = "ros"
- Output:
3
85. Maximal Rectangle
Problem Description
Given a 2D binary matrix filled with 0’s and 1’s, find the largest rectangle containing all ones.
Solution
def maximalRectangle(matrix: list[list[str]]) -> int:
"""
Returns the area of the maximal rectangle.
Args:
matrix (list[list[str]]): A 2D binary matrix.
Returns:
int: The area of the maximal rectangle.
"""
if not matrix:
return 0
n = len(matrix[0])
height = [0] * (n + 1)
result = 0
for row in matrix:
for i in range(n):
height[i] = height[i] + 1 if row[i] == '1' else 0
stack = [-1]
for i in range(n + 1):
while height[i] < height[stack[-1]]:
h = height[stack.pop()]
w = i - 1 - stack[-1]
result = max(result, h * w)
stack.append(i)
return result
Time and Space Complexity
- Time complexity: O(m * n), where m and n are the dimensions of the matrix.
- Space complexity: O(n), as we use a stack.
Sample Input and Output
- Input:
matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
- Output:
6
118. Pascal’s Triangle
Problem Description
Given a non-negative integer numRows
, generate the first numRows
of Pascal’s triangle.
Solution
def generate(numRows: int) -> list[list[int]]:
"""
Returns the first numRows of Pascal's triangle.
Args:
numRows (int): The number of rows.
Returns:
list[list[int]]: The first numRows of Pascal's triangle.
"""
triangle = [[1] * (i + 1) for i in range(numRows)]
for i in range(2, numRows):
for j in range(1, i):
triangle[i][j] = triangle[i - 1][j - 1] + triangle[i - 1][j]
return triangle
Time and Space Complexity
- Time complexity: O(numRows^2), where numRows is the number of rows.
- Space complexity: O(numRows^2), as we generate the entire triangle.
Sample Input and Output
- Input:
numRows = 5
- Output:
[[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
120. Triangle
Problem Description
Given a triangle, find the minimum path sum from top to bottom.
Solution
def minimumTotal(triangle: list[list[int]]) -> int:
"""
Returns the minimum path sum.
Args:
triangle (list[list[int]]): A triangle.
Returns:
int: The minimum path sum.
"""
n = len(triangle)
dp = [[0] * len(row) for row in triangle]
dp[0][0] = triangle[0][0]
for i in range(1, n):
dp[i][0] = dp[i - 1][0] + triangle[i][0]
dp[i][-1] = dp[i - 1][-1] + triangle[i][-1]
for j in range(1, len(triangle[i]) - 1):
dp[i][j] = triangle[i][j] + min(dp[i - 1][j - 1], dp[i - 1][j])
return min(dp[-1])
Time and Space Complexity
- Time complexity: O(n^2), where n is the number of rows in the triangle.
- Space complexity: O(n^2), as we use a 2D DP array.
Sample Input and Output
- Input:
triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
- Output:
11
122. Best Time to Buy and Sell Stock II
Problem Description
You are given an array prices
where prices[i]
is the price of a given stock on the i-th day. Find the maximum profit.
Solution
def maxProfit(prices: list[int]) -> int:
"""
Returns the maximum profit.
Args:
prices (list[int]): A list of prices.
Returns:
int: The maximum profit.
"""
result = 0
for i in range(1, len(prices)):
if prices[i] > prices[i - 1]:
result += prices[i] - prices[i - 1]
return result
Time and Space Complexity
- Time complexity: O(n), where n is the number of days.
- Space complexity: O(1), as we only use a constant amount of space.
Sample Input and Output
- Input:
prices = [7,1,5,3,6,4]
- Output:
7
123. Best Time to Buy and Sell Stock III
Problem Description
You are given an array prices
where prices[i]
is the price of a given stock on the i-th day. Find the maximum profit.
Solution
def maxProfit(prices: list[int]) -> int:
"""
Returns the maximum profit.
Args:
prices (list[int]): A list of prices.
Returns:
int: The maximum profit.
"""
buy1, buy2 = float('-inf'), float('-inf')
sell1, sell2 = 0, 0
for price in prices:
buy1 = max(buy1, -price)
sell1 = max(sell1, buy1 + price)
buy2 = max(buy2, sell1 - price)
sell2 = max(sell2, buy2 + price)
return sell2
Time and Space Complexity
- Time complexity: O(n), where n is the number of days.
- Space complexity: O(1), as we only use a constant amount of space.
Sample Input and Output
- Input:
prices = [3,3,5,0,0,3,1,4]
- Output:
6
131. Palindrome Partitioning
Problem Description
Given a string s
, partition s
such that every substring of the partition is a palindrome.
Solution
def partition(s: str) -> list[list[str]]:
"""
Returns all possible palindrome partitions.
Args:
s (str): The input string.
Returns:
list[list[str]]: All possible palindrome partitions.
"""
def is_palindrome(s: str) -> bool:
return s == s[::-1]
def backtrack(start: int, path: list[str]):
if start == len(s):
result.append(path[:])
return
for end in range(start, len(s)):
if is_palindrome(s[start:end + 1]):
path.append(s[start:end + 1])
backtrack(end + 1, path)
path.pop()
result = []
backtrack(0, [])
return result
Time and Space Complexity
- Time complexity: O(2^n * n), where n is the length of the string.
- Space complexity: O(n), as we use a recursion stack.
Sample Input and Output
- Input:
s = "aab"
- Output:
[["a","a","b"],["aa","b"]]
152. Maximum Product Subarray
Problem Description
Given an integer array nums
, find the maximum product subarray.
Solution
def maxProduct(nums: list[int]) -> int:
"""
Returns the maximum product.
Args:
nums (list[int]): A list of integers.
Returns:
int: The maximum product.
"""
max_product = min_product = result = nums[0]
for i in range(1, len(nums)):
if nums[i] < 0:
max_product, min_product = min_product, max_product
max_product = max(nums[i], max_product * nums[i])
min_product = min(nums[i], min_product * nums[i])
result = max(result, max_product)
return result
Time and Space Complexity
- Time complexity: O(n), where n is the length of the array.
- Space complexity: O(1), as we only use a constant amount of space.
Sample Input and Output
- Input:
nums = [2,3,-2,4]
- Output:
6
188. Best Time to Buy and Sell Stock IV
Problem Description
You are given an array prices
where prices[i]
is the price of a given stock on the i-th day. Find the maximum profit.
Solution
def maxProfit(k: int, prices: list[int]) -> int:
"""
Returns the maximum profit.
Args:
k (int): The maximum number of transactions.
prices (list[int]): A list of prices.
Returns:
int: The maximum profit.
"""
if k >= len(prices) // 2:
return sum(max(0, b - a) for a, b in zip(prices, prices[1:]))
dp = [[0] * len(prices) for _ in range(k + 1)]
for i in range(1, k + 1):
max_diff = -prices[0]
for j in range(1, len(prices)):
dp[i][j] = max(dp[i][j - 1], prices[j] + max_diff)
max_diff = max(max_diff, dp[i - 1][j] - prices[j])
return dp[k][-1]
Time and Space Complexity
- Time complexity: O(k * n), where k is the maximum number of transactions and n is the number of days.
- Space complexity: O(k * n), as we use a 2D DP array.
Sample Input and Output
- Input:
k = 2, prices = [3,3,5,0,0,3,1,4]
- Output:
6
221. Maximal Square
Problem Description
Given a 2D binary matrix filled with 0’s and 1’s, find the largest square.
Solution
def maximalSquare(matrix: list[list[str]]) -> int:
"""
Returns the side length of the maximal square.
Args:
matrix (list[list[str]]): A 2D binary matrix.
Returns:
int: The side length of the maximal square.
"""
if not matrix:
return 0
m, n = len(matrix), len(matrix[0])
dp = [[0] * n for _ in range(m)]
result = 0
for i in range(m):
for j in range(n):
if matrix[i][j] == '1':
if i == 0 or j == 0:
dp[i][j] = 1
else:
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1
result = max(result, dp[i][j])
return result * result
Time and Space Complexity
- Time complexity: O(m * n), where m and n are the dimensions of the matrix.
- Space complexity: O(m * n), as we use a 2D DP array.
Sample Input and Output
- Input:
matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
- Output:
4
264. Ugly Number II
Problem Description
An ugly number is a positive integer whose prime factors are limited to 2, 3, and 5.
Solution
def nthUglyNumber(n: int) -> int:
"""
Returns the nth ugly number.
Args:
n (int): The index.
Returns:
int: The nth ugly number.
"""
ugly = [1]
i2, i3, i5 = 0, 0, 0
while len(ugly) < n:
next_ugly = min(ugly[i2] * 2, ugly[i3] * 3, ugly[i5] * 5)
ugly.append(next_ugly)
if next_ugly == ugly[i2] * 2:
i2 += 1
if next_ugly == ugly[i3] * 3:
i3 += 1
if next_ugly == ugly[i5] * 5:
i5 += 1
return ugly[-1]
Time and Space Complexity
- Time complexity: O(n), where n is the index.
- Space complexity: O(n), as we store the first n ugly numbers.
Sample Input and Output
- Input:
n = 10
- Output:
12
279. Perfect Squares
Problem Description
Given an integer n
, return the least number of perfect square numbers.
Solution
def numSquares(n: int) -> int:
"""
Returns the least number of perfect square numbers.
Args:
n (int): The input number.
Returns:
int: The least number of perfect square numbers.
"""
dp = [float('inf')] * (n + 1)
dp[0] = 0
for i in range(1, n + 1):
j = 1
while j * j <= i:
dp[i] = min(dp[i], dp[i - j * j] + 1)
j += 1
return dp[n]
Time and Space Complexity
- Time complexity: O(n^(3/2)), where n is the input number.
- Space complexity: O(n), as we use a DP array.
Sample Input and Output
- Input:
n = 12
- Output:
3
300. Longest Increasing Subsequence
Problem Description
Given an integer array nums
, find the length of the longest increasing subsequence.
Solution
def lengthOfLIS(nums: list[int]) -> int:
"""
Returns the length of the longest increasing subsequence.
Args:
nums (list[int]): A list of integers.
Returns:
int: The length of the longest increasing subsequence.
"""
if not nums:
return 0
dp = [1] * len(nums)
for i in range(1, len(nums)):
for j in range(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)
Time and Space Complexity
- Time complexity: O(n^2), where n is the length of the array.
- Space complexity: O(n), as we use a DP array.
Sample Input and Output
- Input:
nums = [10,9,2,5,3,7,101,18]
- Output:
4
312. Burst Balloons
Problem Description
Given an array of integers representing the numbers on the balloons.
Solution
def maxCoins(balloons: list[int]) -> int:
"""
Returns the maximum coins.
Args:
balloons (list[int]): A list of integers.
Returns:
int: The maximum coins.
"""
balloons = [1] + [x for x in balloons] + [1]
dp = [[0] * len(balloons) for _ in range(len(balloons))]
for length in range(2, len(balloons)):
for left in range(len(balloons) - length):
right = left + length
for i in range(left + 1, right):
dp[left][right] = max(dp[left][right], balloons[left] * balloons[i] * balloons[right] + dp[left][i] + dp[i][right])
return dp[0][len(balloons) - 1]
Time and Space Complexity
- Time complexity: O(n^3), where n is the number of balloons.
- Space complexity: O(n^2), as we use a 2D DP array.
Sample Input and Output
- Input:
balloons = [3,1,5,8]
- Output:
167
337. House Robber III
Problem Description
The thief has a list of houses.
Solution
class Solution:
def rob(self, root: Optional[TreeNode]) -> int:
"""
Returns the maximum amount.
Args:
root (Optional[TreeNode]): The root of the tree.
Returns:
int: The maximum amount.
"""
def dfs(node):
if not node:
return 0, 0
left_no_rob, left_rob = dfs(node.left)
right_no_rob, right_rob = dfs(node.right)
no_rob = max(left_no_rob, left_rob) + max(right_no_rob, right_rob)
rob = node.val + left_no_rob + right_no_rob
return no_rob, rob
return max(dfs(root))
Time and Space Complexity
- Time complexity: O(n), where n is the number of nodes.
- Space complexity: O(h), as we use a recursion stack.
Sample Input and Output
- Input:
root = [3,2,3,null,3,null,1]
- Output:
7