- Principles
- Problems
- LeetCode
- 53. Maximum Subarray
- 121. Best Time to Buy and Sell Stock
- 122. Best Time to Buy and Sell Stock II
- 5. Longest Palindromic Substring
- 647. Palindromic Substrings
- 516. Longest Palindromic Subsequence
- 10. Regular Expression Matching
- 42. Trapping Rain Water
- 44. Wildcard Matching
- 55. Jump Game
- 45. Jump Game II
- 1306. Jump Game III
- 1871. Jump Game VII
- 1345. Jump Game IV
- 62. Unique Paths
- 63. Unique Paths II
- 64. Minimum Path Sum
- 70. Climbing Stairs
- 746. Min Cost Climbing Stairs
- 72. Edit Distance
- 85. Maximal Rectangle
- 84. Largest Rectangle in Histogram
- 87. Scramble String
- 312. Burst Balloons
- 1246. Palindrome Removal
- 322. Coin Change
- 518. Coin Change 2
- 416. Partition Equal Subset Sum
- 96. Unique Binary Search Trees
- 91. Decode Ways
- 639. Decode Ways II
- 97. Interleaving String
- 115. Distinct Subsequences
- 118. Pascal’s Triangle
- 119. Pascal’s Triangle II
- 120. Triangle
- 123. Best Time to Buy and Sell Stock III
- 188. Best Time to Buy and Sell Stock IV
- 309. Best Time to Buy and Sell Stock with Cooldown
- 131. Palindrome Partitioning
- 132. Palindrome Partitioning II
- 1278. Palindrome Partitioning III
- 1745. Palindrome Partitioning IV
- 139. Word Break
- 140. Word Break II
- 472. Concatenated Words
- 152. Maximum Product Subarray
- 174. Dungeon Game
- 198. House Robber
- 213. House Robber II
- 221. Maximal Square
- 233. Number of Digit One
- 263. Ugly Number
- 264. Ugly Number II
- 313. Super Ugly Number
- 1201. Ugly Number III
- 279. Perfect Squares
- 204. Count Primes
- 300. Longest Increasing Subsequence
# Principles
- Determine the dp array (dp table) and the meaning of the subscript
- Determine the recurrence formula
- How to initialize the dp array
- Determine the traversal order
- Derive the dp array by example
# Problems
# Knapsack Problem
# 0-1 Knapsack
416. Partition Equal Subset Sum Valid.
# Complete Knapsack
322. Coin Change Minimum weight.
518. Coin Change 2 Total Number
# Interval DP
# Palindrome
1 | |
5. Longest Palindromic Substring Check if valid. Contiguous.
647. Palindromic Substrings Find the number of all possible combinations. Contiguous.
516. Longest Palindromic Subsequence Find the length of longest subsequence. Not contiguous.
# Two Strings
# Matching
10. Regular Expression Matching Matching. Valid.
1 | |
44. Wildcard Matching Matching. Valid.
1 | |
1 | |
# Histogram
42. Trapping Rain Water. Area. Bidirectional Search. DP(Memorized Search). Two pointer. Monolithic Stack.
84. Largest Rectangle in Histogram. Area. Bidirectional Search. DP(Memorized Search). Monolithic Stack.
85. Maximal Rectangle. Area. Based on 84 for each Row. DP(Memorized Search based on last row).
# Subarray
# Maximum sum
# Stock
State Machine/Recursive Formula
121. Best Time to Buy and Sell Stock
122. Best Time to Buy and Sell Stock II
123. Best Time to Buy and Sell Stock III
1 | |
188. Best Time to Buy and Sell Stock IV
1 | |
309. Best Time to Buy and Sell Stock with Cooldown
1 | |
# Robber
State Machine
198. House Robber
213. House Robber II
# Combination
62. Unique Paths Combinatorial number. DP.
63. Unique Paths II With Obstacle.
70. Climbing Stairs 1 or 2 steps. What if 1…N Steps. Find all combinations.
746. Min Cost Climbing Stairs Options with cost. Find the minimum cost.
# Path/DFS
64. Minimum Path Sum Find the minimum path.
87. Scramble String Check path valid.
174. Dungeon Game Backtrack
# Divide & Conquer
312. Burst Balloons
1246. Palindrome Removal
# Jump Game
55. Jump Game
45. Jump Game II
1306. Jump Game III BFS
1345. Jump Game IV BFS cached iterative
# LeetCode
# 53. Maximum Subarray
A general dp solution.
The question is quite clear. I only need to return the maximum sum rather the specific subarray.
The first method comes into my mind is the dynamic programming. The maximum sum of the subarray with the index from 0 to N - 1, in which N is the length of the input array, is the larger one between the sum of the subarray with the index between 0 to N-2 adding the N-1 th element, and the N-1 th element which means reset the sum. So we can find the maximum sum of the subarray during the iteration.
Therefore, we can determine the dp array which means the maximum sum of the subarray between the index 0 to subscript i.
And the recurrence formula is:
1 | |
And initializing the dp array. The dp[0] means the maximum array from 0 to 0, which exactly is nums[0].
The traversal order is incremental.
And manually validate our assumptions with an example.
1 | |
A greedy solution
We keep a sum of the contiguous subarray adding each new element. Once the sum is less than 0, we reset the sum to 0. Any negative number could reduces the sum, and if the sum is already negative, we’re choosing the large one between the sum of the contiguous subarray and the next element, or simply the next element. The larger one must be the later, which is exactly resetting the sum to 0.
1 | |
# 121. Best Time to Buy and Sell Stock
Greedy Solution
The constraint is I have to sell the stock in a day after buying. So the first method could be finding the minimum value from the left and the maximum value from the right. And the difference between these two numbers is the maximum profit.
1 | |
optimized
1 | |
# 122. Best Time to Buy and Sell Stock II
Greedy Solution
Since we could only hold one share of stock and we can buy and sell at any time, so we could break the profit down to a single day, and we only count the days which have a positive profit.
1 | |
# 5. Longest Palindromic Substring
DP
Is the substring contiguous? Yes
If multiple substrings are found, which one should I return? Any of them.
The solution I may have a try is dynamic programming.
First, determine the meaning of dp table dp[i][j], which means is the substring from the subscript i to j is palindromic. i is less then j. The interval is left closed and right closed.
Second, determine the recursive formula. If s[i] and s[j] are not equal, there is no chance the substring could be palindromic. Therefore, we discuss the details where s[i] and s[j] are equal. There are 3 situations.
- If i == j, then the substring is a letter, so it must be palindromic.
- If j - i == 1, then the substring is like ‘aa’, so it is palindromic.
- If j - i > 1, then the substring is depending on dp[i+1][j-1].
Third, determine how to initialize the dp table. dp[0][0] is palindromic, initialize all other elements to false.
Fourth, determine the traversal order.
dp[i + 1][j - 1] is at the bottom left of dp[i][j], so dp[i + 1][j - 1] must be determined before dp[i][j]. So it must be traversed from bottom to top and left to right, so as to ensure that dp[i + 1][j - 1] is calculated.
1 | |
# 647. Palindromic Substrings
This is similar to 5.
DP
1 | |
# 516. Longest Palindromic Subsequence
This is quite similar to 5 other than the situation analysis.
First, determine the meaning of dp table dp[i][j], which means the length of the longest palindromic subsequence from subscript i to j.
Second, determine the recursive formula. If s[i] == s[j], then dp[i][j] = dp[i + 1][j - 1] + 2. If s[i] != s[j], it means that adding s[i] and s[j] at the same time cannot increase the length of the palindrome substring in the interval [i, j], then add s[i] and s respectively. [j] See which one can form the longest palindromic subsequence. The dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]).
Third, initialize the dp array. dp[i][i] = 1.
Fourth, determine the traversal order. dp[i][j] is dependent on dp[i + 1][j - 1] and dp[i + 1][j] and dp[i][j - 1]. So it must be traversed from bottom to top and left to right.
1 | |
# 10. Regular Expression Matching
Simply converted to a dp problem.
First, determine the dp table, dp[i][j] means is p[0:j-1] matches s[0:i-1].
Second, determine the recursive formula.
- If current p (p[j - 1]) is star, dp[i][j] compares s[i-1] and p[i-1]
- because * means matching 0 or more, dp[i][j] at least equals to dp[i][j-2], which ignores the last two characters of p.
- If p[i-2] is dot, then the condition is relax to dp[i - 1][j - 2].
- And if s[i-1] == p[j - 2], current s equals last p, then dp[i][j] = dp[i - 1][j], check if we remove the last character of s, if the rest of s matches p.
- If current p is dot, which could match any character, dp[i][j] = dp[i - 1][j - 1], if the shorter substring matches, then current substring matches.
- If current p equals to current s, dp[i][j] = dp[i - 1][j - 1], if the shorter substring matches, then current substring matches.
- One optimization is if the next character is star, just skip the current comparison.
Third initialize the dp table.
dp[0][0] must be true.
Matches the 0-length s with p.
Determine the traversal order. From left to right, bottom to up.
1 | |
# 42. Trapping Rain Water
Column Searching
The main idea is to find the maximum wall from the target column i. The first and the last wall do not count.
1 | |
Dynamic Programming
Use an array to store the max height.
maxLeft[i] = max(height[i], maxLeft[i - 1]);
maxRight[i] = max(height[i], maxRight[i + 1]);
1 | |
Double Pointer
Optimization to DP Solution.
1 | |
# 44. Wildcard Matching
This is similar to 10 but simpler because the wildcard character are not combined.
Consider it as a dp problem.
First determine the dp table, dp[i][j] means whether p[i-1] can match s[i-1].
- If current p[i-1] is star, then dp[i][j] compares s[i-1] and p[j-1].
- star matches 0 or more characters. dp[i][j] at least equals to dp[i][j-1], which ignores the last characters of p or dp[i][j] equals to dp[i-1][j], which ignores the last character of s.
- If current p[i-1] is dot, which matches any single character. dp[i][j] = dp[i-1][j-1].
- If current p[i-1] equals s[i-1], which matches any single character. dp[i][j] = dp[i-1][j-1].
1 | |
# 55. Jump Game
Greedy
Let’s say the range of current jump is [curBegin, curEnd], curFarthest is the farthest point that all points is [curBegin, curEnd] can reach. Once the current point reaches curEnd, then trigger another jump and set the new curEnd with curFarthest.
1 | |
DP
1 | |
# 45. Jump Game II
Dynamic Programming
This can be converted to a dp problem.
First, determine dp array, dp[i] means the minimum number of jumps from n - 1 to i.
Second, determine the iteration formula. For each index i, iterate j over i + 1 to maxJump, in which maxJump equals i + nums[i]. dp[i] = min(dp[i], dp[j] + 1).
Third, Initialize dp[n-1] = 0, because we only need to jump 0 times to n-1.
Fourth, determine the traversal order. We need two variables, i iterates from n - 2 to 0, j iterates from i + 1 to the maxJump of i.
1 | |
Greedy
Let’s say the range of current jump is [curBegin, curEnd], curFarthest is the farthest point that all points is [curBegin, curEnd] can reach. Once the current point reaches curEnd, then trigger another jump and set the new curEnd with curFarthest.
1 | |
# 1306. Jump Game III
At first glance, we could use BFS search. Because you can only jump to either i + arr[i] or i - arr[i], the algorithm complexity is O(2N). Because the arr is non-negative, we can use its negativity marked as traversed.
1 | |
# 1871. Jump Game VII
It’s a bottom-up DP implementation. The boolean value represents whether this position is reachable from start. So the first step is to generate the table. Here the table was pre-labeled True or False, thus '1’s are already labeled False.
To determine the state of dp[i], one need to check the states in window dp[i-maxJ : i-minJ], because any one of them can reach i if it’s labeled True.
Then you need to check if there is a True in this window. Notice that this is a sliding window problem, so you don’t need to calculate it everytime. You only need to remove the effect from dp[i-maxJ-1] and add the dp[i-minJ]. This is done by these two lines of code pre += dp[i - minJ] and pre -= dp[i - maxJ - 1]
The if statements if i >= minJ: and if i > maxJ: are dealing with the initial boundary.
1 | |
# 1345. Jump Game IV
We can try brute force bfs search first. But time limit exceeds.
1 | |
Optimization with iteration. The first one reaches the end is the minimum number of steps.
The first visit is the minimum number of steps. Clear the indices map to avoid revisiting.
1 | |
# 62. Unique Paths
It’s a typical combinatorial problem, which should be:
But we can also use DP to solve this problem.
First determine the dp array, dp[i][j] means the number of possible unique paths to the ixj grid.
Second, determine the iteration formula. dp[i][j] is dependent on dp[i-1][j] and dp[i][j-1]. dp[i][j] = dp[i-1][j] + dp[i][j-1]
Third, initialize the array. dp[0][0] = 1. And grids with 1 width or 1 height only have 1 possible unique paths.
Fourth, determine the traversal order. It must be from left to right, and bottom to top.
1 | |
# 63. Unique Paths II
It’s similar to 62, but be cautious to the initialization of the array. And if dp[i][j] is occupied by an obstacle, there is no path to it.
1 | |
# 64. Minimum Path Sum
A dp problem.
1 | |
# 70. Climbing Stairs
It’s basically fibonacci problem.
1 | |
# 746. Min Cost Climbing Stairs
It could be solved by dp.
First dp[i] means the minimum cost to reach the ith floor.
Second dp[i] = min(dp[i-1], dp[i-2]) + cost[i]
Third initialize dp[0] = cost[0], dp[1] = cost[1]
Fourth traverse from left to right.
Finally return the minimum of the last two dp elements.
1 | |
# 72. Edit Distance
It’s DP problem.
First dp[i][j] means the minimum operations between the first i - 1 characters in word1 and the first j - 1 characters in word2.
Second, if word1[i - 1] == word2[i - 2], there is no need to edit any characters. Otherwise, there’s different operations:
- word1 deletes an character, so the left i - 2 characters of word1 equals j - 1 characters of word2, dp[i][j] = dp[i - 1][j]
- word2 deletes an character, so the left i - 1 characters of word1 equals j - 2 characters of word2, dp[i][j] = dp[i][j - 1]
- the addition of character is the reversion of deletion.
- substitution, replace word1[i - 1] to match word2[j - 1], dp[i][j] = dp[i - 1][j - 1] + 1
Third, initialize dp[0][0] = 0. For word1’s first i - 1 characters, there must be i operations to the empty word2.
Fourth, left to right. Bottom to up.
1 | |
# 85. Maximal Rectangle
DP
Try to find the highest height and the longest right - left distance for each row.
1 | |
# 84. Largest Rectangle in Histogram
Stack
The basic idea is traversing from left to right. There are 4 possibilities:
- There’s a rectangle forming just using the height of the current bar which has an area larger than the maxArea previously recorded.
- There’s a rectangle forming using the width or entire spread of the area starting from a bar seen long back which has an area larger than the current maxArea
- There’s a rectangle forming using width and height of recent tall bars which has an area larger than the current maxArea.
- There’s no rectangle with larger area at this step.
So we have to find the possible bars which may form a larger rectangle and eliminate the impossible bars when we encounter a bar less than the bar we’ve before. So the stack is come into my mind.
1 | |
Two pointer
To find the last index from both the left and right where heights[l] >= heights[i] and heights[r] >= heights[i]
1 | |
# 87. Scramble String
Recursive
Straight forward way to check all possible substring by dividing the string into two parts and check whether both two parts are scrambled.
But unfortunately, simple recursive solution will get a time limit exceed error. We have to optimize using premature exit and memorization.
1 | |
DP
DP is generalized from the memorized recursive solution. The dp[i][j][k] means whether the k length substring from i, j respectively are scramble strings.
1 | |
# 312. Burst Balloons
We could describe the whole process in reverse. Suppose we have a open interval (i, j), and the kth ballon is the last ballon which will be bursted. Then the coins we can get is:
nums[i] * nums[j] * nums[k] + coins(i, k) + coins(k, j)
Determine the traversal order.
1 | |
# 1246. Palindrome Removal
It’s problem to find the minimal number of operations in an interval.
dp[i][j] means the minimal number of delete operations in the interval [i, j], left closed, right closed.
If i == j, the dp[i][i] = 1, there only needs 1 operation, because there is only one character.
If j - i == 1, if the arr[i] == arr[j], dp[i][j] = 1, otherwise dp[i][j] = 2
If j - i > 1, we have to check if arr[i] == arr[j]. If so, dp[i][j] = dp[i + 1][j - 1]. Otherwise, we could use divide and conquer. Suppose k divides interval into [i,k][k,j], we need to find the least dp[i][k] + dp[k+1][j].
Determine the traversal order. It can be the outer iterate from 0 to n, the inner loop iterate from i to 0. Or the outer loop iterate from n to 0, the inner loop iterate from i to n.
1 | |
# 322. Coin Change
Review the complete knapsack problem.
1 | |
# 518. Coin Change 2
This is a complete knapsack problem.
Here, we are look for the combination number.
1 | |
But what if we are look for the permutation number.
We use the same coin more than once.
1 | |
# 416. Partition Equal Subset Sum
The problem can be converted to whether we can find a subarray with the sum of total_sum / 2, which is a 0-1 knapsack problem. If we can fill the sum/2 weighted knapsack with sum/2 valued items.
1 | |
# 96. Unique Binary Search Trees
for n = 1 there is 1 tree
dp[1] = dp[0] * dp[0]
for n = 2 there is 2 tree
dp[2] = dp[0] * dp[1] + dp[1] * dp[0]
for n = 3 there is 5 tree
dp[3] = dp[0] * dp[2] + dp[1] * dp[1] + dp[2] * dp[0]
1 | |
# 91. Decode Ways
Recursion
Time Complexity O(2^N)
1 | |
Memorization
Time Complexity O(N)
Space Complexity O(N)
1 | |
DP
Time Complexity O(N)
Space Complexity o(N)
Copy from memorization
1 | |
bottom up
1 | |
DP Constant Space
Time Complexity O(N)
Space Complexity O(1)
1 | |
# 639. Decode Ways II
1 | |
# 97. Interleaving String
Recursive
1 | |
DP
1 | |
# 115. Distinct Subsequences
1 | |
# 118. Pascal’s Triangle
1 | |
# 119. Pascal’s Triangle II
DP
1 | |
Extra ith Space
1 | |
# 120. Triangle
DP
1 | |
O(N) SPACE
1 | |
# 123. Best Time to Buy and Sell Stock III
DP
It’s not difficult to get the DP recursive formula:
dp[k, i] = max(dp[k, i-1], prices[i] - prices[j] + dp[k-1, j-1]), j=[0…i-1]
For k transactions, on i-th day, if we don’t trade then the profit is same as previous day dp[k, i-1];
and if we bought the share on j-th day where j=[0…i-1], then sell the share on i-th day then the profit is prices[i] - prices[j] + dp[k-1, j-1] .
Actually j can be i as well. When j is i, the one more extra item prices[i] - prices[j] + dp[k-1, j] = dp[k-1, i] looks like we just lose one chance of transaction.
I see someone else use the formula dp[k, i] = max(dp[k, i-1], prices[i] - prices[j] + dp[k-1, j]), where the last one is dp[k-1, j] instead of dp[k-1, j-1]. It’s not the direct sense, as if the share was bought on j-th day, then the total profit of previous transactions should be done on (j-1)th day. However, the result based on that formula is also correct, because if the share was sold on j-th day and then bought again, it is the same if we didn’t trade on that day.
Time Limit Exceeded
Time complexity is O(kn^2), space complexity is O(kn).
1 | |
Remove duplicated calculation
Time complexity is O(kn), space complexity is O(kn).
1 | |
Swap the loops
Time complexity is O(kn), space complexity is O(kn + k).
1 | |
Compact Dimension
Time Complexity O(kN)
Space Complexity O(2k)
1 | |
Name Expansion
# 188. Best Time to Buy and Sell Stock IV
1 | |
# 309. Best Time to Buy and Sell Stock with Cooldown
s0 NONE <- s0, s2
s1 BOUGHT <- s1, s0 - prices[i]
s2 COOL DOWN <- s1 + prices[i]
1 | |
# 131. Palindrome Partitioning
Backtracking DFS
1 | |
DP DFS
1 | |
DP
1 | |
# 132. Palindrome Partitioning II
- cut[i] is the minimum of cut[j - 1] + 1 (j <= i) if [j, i] is palindrome
- If [j, i] is palindrome, [j + 1, i - 1] is palindrome, and cut[j] == cut[i].
1 | |
# 1278. Palindrome Partitioning III
Time Complexity O(N^2)
1 | |
# 1745. Palindrome Partitioning IV
We can now check, if left, mid and right are all palindrom, and return true.
1 | |
# 139. Word Break
BackTracking With Mem
Time Complexity O(N^2), Worst O(2^N)
1 | |
DP
1 | |
# 140. Word Break II
Backtracking Filtered With Set
1 | |
Without Set
1 | |
# 472. Concatenated Words
Combined with 140.
DP
1 | |
BackTracking
1 | |
# 152. Maximum Product Subarray
Greedy
1 | |
# 174. Dungeon Game
Backtracking
LTE
1 | |
Backtracking With Memorization
1 | |
DP
1 | |
# 198. House Robber
Similar to stock trading with 2 states: rob or skip.
1 | |
# 213. House Robber II
Either robbing from the first n-1 houses or the last n-1 houses.
1 | |
# 221. Maximal Square
This is quite similar to Maximal Rectangle but much easier, because square only depends on the three directions.
1 | |
# 233. Number of Digit One
TAT
1 | |
(1) xyz * 1000 if d == 0
(2) xyz * 1000 + abc + 1 if d == 1
(3) xyz * 1000 + 1000 if d > 1
1 | |
# 263. Ugly Number
Keep divided by 2,3,5 until not divisible, check whether the quotient is 1.
4 is 2x2.
1 | |
# 264. Ugly Number II
1 | |
# 313. Super Ugly Number
1 | |
# 1201. Ugly Number III
1 | |
# 279. Perfect Squares
1 | |
1 | |
# 204. Count Primes
Basic idea to find prime factor.
1 | |
# 300. Longest Increasing Subsequence
Subsequence is not continuous.
1 | |