Skip to content

Commit 8242fc5

Browse files
authored
Problems/dp (#25)
* Update linked list solutions * Add 509. Fibonacci Number solution * Update dynamic programming notes * Update dp and stack note * Add stack & queue problem solutions list * Update dp note * Add 70. Climbing Stairs solution * Add 746. Min Cost Climbing Stairs solution * Update 746. Min Cost Climbing Stairs solution note * Add 62. Unique Paths solution * Add 62. Unique Paths DFS solution * Update 20. Valid Parentheses solution * Update the solutions to some stack problem * Add 63. Unique Paths II solution * Add 96. Unique Binary Search Trees solution * Update 1249. Minimum Remove to Make Valid Parentheses solution * Update stack solutions * Add dynamic programming solution to 121. Best Time to Buy and Sell Stock * Update 394. Decode String solution * Update 121. Best Time to Buy and Sell Stock dp note * Add 122. Best Time to Buy and Sell Stock II dp solution * Update dp note for Best Time to Buy and Sell Stock series problems * Update knapsack problems note * Add 416. Partition Equal Subset Sum solution * Add 494. Target Sum solution * Add unbounded knapsack problem note * Add 322. Coin Change different kinds of solutions * Fix the 0/1 knapsack problem implementation * Add 518. Coin Change 2 solution * Add 198. House Robber solution * Add 213. House Robber II solution * Add 337. House Robber III solutions * Add longest common subsequence note to dp * Add 300. Longest Increasing Subsequence solution * Update tree problem solutions * Add 516. Longest Palindromic Subsequence solution * Update 300. Longest Increasing Subsequence solution * Add 674. Longest Continuous Increasing Subsequence solution * Add 718. Maximum Length of Repeated Subarray solution * Add 5. Longest Palindromic Substring dp solution * Update 516. Longest Palindromic Subsequence solution * Update 101. Symmetric Tree solution * Update 53. Maximum Subarray solution * Add 152. Maximum Product Subarray solution * Add 55. Jump Game solution * Update 55. Jump Game solution * Add 139. Word Break solution * Add 64. Minimum Path Sum solution * Add 279. Perfect Squares solution * Add 647. Palindromic Substrings solutiong * Update 543. Daimeter of Binary Tree solution * Add 264. Ugly Number II solutions * Fix typos in greedy note, update dp problem list * Update DP note for knapsack problem of all implementation and solution for space optimization
1 parent beb6161 commit 8242fc5

File tree

62 files changed

+3736
-524
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+3736
-524
lines changed

leetcode/101.symmetric-tree.md

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,26 @@ fun check(left: TreeNode?, right: TreeNode?): Boolean {
1515

1616
### Iterative
1717
```kotlin
18+
val emptyNode = TreeNode(-1)
1819
fun isSymmetric(root: TreeNode?): Boolean {
1920
if (root == null) return true
20-
else {
21-
val queue = ArrayDeque<TreeNode>()
22-
if (root?.left != null) queue.add(root?.left)
23-
if (root?.right != null) queue.add(root?.right)
21+
val queue = ArrayDeque<TreeNode>()
22+
if (root.left != null) queue.addLast(root.left)
23+
if (root.right != null) queue.addLast(root.right)
24+
while (queue.isNotEmpty()) {
25+
val left = queue.removeFirst()
26+
if (queue.isEmpty()) return false
27+
val right = queue.removeFirst()
2428

25-
while (!queue.isEmpty()) {
26-
val left = queue.removeFirstOrNull()
27-
val right = queue.removeFirstOrNull()
28-
29-
if (left == null && right == null) continue
30-
if (left == null || right == null || left?.`val` != right?.`val`) return false
31-
32-
// We have to push in the symmetric order
33-
queue.addLast(left!!.left)
34-
queue.addLast(right!!.right)
29+
if (left == emptyNode && right == emptyNode) continue
30+
if (left == emptyNode || right == emptyNode || left.`val` != right.`val`) return false
3531

36-
queue.addLast(left!!.right)
37-
queue.addLast(right!!.left)
38-
}
39-
return true
32+
// Make sure to enqueue empty node
33+
if (left.left != null) queue.addLast(left.left!!) else queue.addLast(emptyNode)
34+
if (right.right != null) queue.addLast(right.right!!) else queue.addLast(emptyNode)
35+
if (left.right != null) queue.addLast(left.right!!) else queue.addLast(emptyNode)
36+
if (right.left != null) queue.addLast(right.left!!) else queue.addLast(emptyNode)
4037
}
38+
return true
4139
}
42-
```
43-
44-
> Idea is correct, but code won't compile successfully, LeetCode jvm doesn't support `removeFirstOrNull()` function.
40+
```

leetcode/104.maximum-depth-of-binary-tree.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,24 @@ private fun max(n1: Int, n2: Int) = if (n1 > n2) n1 else n2
1111
```
1212

1313
* **Time Complexity**: `O(n)`, iterate all nodes.
14-
* **Space Complextiy**: `O(height)`, height might be `lg n` (balanced) or `n` (skewed)
14+
* **Space Complextiy**: `O(height)`, height might be `lg n` (balanced) or `n` (skewed)
15+
16+
### BFS
17+
```kotlin
18+
fun maxDepth(root: TreeNode?): Int {
19+
if (root == null) return 0
20+
val queue = ArrayDeque<TreeNode>()
21+
var depth = 0
22+
queue.addLast(root)
23+
while (queue.isNotEmpty()) {
24+
val size = queue.size
25+
for (i in 0 until size) {
26+
val node = queue.removeFirst()
27+
if (node.left != null) queue.addLast(node.left!!)
28+
if (node.right != null) queue.addLast(node.right!!)
29+
}
30+
depth++
31+
}
32+
return depth
33+
}
34+
```

leetcode/121.best-time-to-buy-and-sell-stock.md

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
## [121. Best Time to Buy and Sell Stock](https://leetcode.com/problems/best-time-to-buy-and-sell-stock/)
22

3-
### Straightforward
4-
Strategies:
5-
1. Look for the lowest buy price: Check current price, if current price < buy price, then update buy price to current one.
6-
2. Look for max profit: If we can sell, then check if the current profit > max profit.
3+
### Greedy
4+
We're going to find minimium price to buy and calculate max profit for every day.
75

86
| | Init | 7 | 1 | 5 | 3 | 6 | 4 |
97
|-----------|------|---|---|---|---|---|---|
@@ -12,25 +10,46 @@ Strategies:
1210

1311
```kotlin
1412
fun maxProfit(prices: IntArray): Int {
15-
var maxProfit = 0
16-
var minPrice = Int.MAX_VALUE
13+
var min: Int? = null
14+
var profit = 0
1715
for (i in 0 until prices.size) {
18-
if (minPrice > prices[i]) {
19-
minPrice = prices[i]
20-
}
21-
22-
val currentProfit = prices[i] - minPrice
23-
if (currentProfit > maxProfit) {
24-
maxProfit = currentProfit
16+
if (min == null) {
17+
min = prices[i]
18+
} else {
19+
if (prices[i] < min) min = prices[i]
20+
val currentProfit = prices[i] - min
21+
profit = if (currentProfit > profit) currentProfit else profit
2522
}
2623
}
27-
return maxProfit
24+
return profit
2825
}
2926
```
3027

3128
* **Time Complexity**: `O(n)`.
3229
* **Space Complexity**: `O(1)`.
3330

31+
### Dynamic Programming
32+
We can apply the framework mentioned on [Dynamic Programming - Best Time to Buy and Sell Stock Problems](../topics/dynamic-programming.md#best-time-to-buy-and-sell-stock-problems), where `k` is 1, so the profit is always 0 when we're going to buy the stock.
33+
34+
```kotlin
35+
fun maxProfit(prices: IntArray): Int {
36+
val n = prices.size
37+
val dp = Array(prices.size) { _ -> IntArray(2) }
38+
// The max profit for Cash state is 0
39+
dp[0][0] = 0
40+
// The max profit for Stock state is the amount we pay for the stock
41+
dp[0][1] = -prices[0]
42+
for (i in 1 until prices.size) {
43+
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i])
44+
45+
// It should be max(dp[i - 1][1], dp[i - 1][0] - prices[i])
46+
// We can only buy once, so the dp[i - 1][0] is always 0 (always buy stock at i-th price)
47+
dp[i][1] = max(dp[i - 1][1], 0 - prices[i])
48+
}
49+
return dp[n - 1][0]
50+
}
51+
```
52+
3453
### Brute Force
3554
```kotlin
3655
fun maxProfit(prices: IntArray): Int {
Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,45 @@
11
## [122. Best Time to Buy and Sell Stock II](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/)
22

3-
We're going to find all the range of that prices go up. (The gree line below)
3+
### Greedy
4+
We're going to find all the range of that prices go up. (The green line below)
45

56
![Prices Chart](../media/122.best-time-to-buy-and-sell-stock-ii.png)
67

8+
We can break the profit by day, if the price of today is greater than previous day, then there will be some profit on that day.
9+
710
```kotlin
811
fun maxProfit(prices: IntArray): Int {
9-
var totalProfit = 0
10-
var buyPrice: Int? = null
11-
12-
var day = 0
13-
while (day < prices.size) {
14-
// Check to buy: I didn't buy before and the price is lower
15-
if (buyPrice == null && (day + 1 < prices.size && prices[day] < prices[day + 1])) {
16-
buyPrice = prices[day]
17-
}
18-
19-
// Check to sell: (price starts to fall down, or it's the last trade day) and I bought before
20-
if (buyPrice != null && (day + 1 == prices.size || (day + 1 < prices.size && prices[day + 1] < prices[day]))) {
21-
val profit = prices[day] - buyPrice
22-
if (profit > 0) {
23-
totalProfit += profit
24-
buyPrice = null
25-
}
12+
var profit = 0
13+
for (i in 1 until prices.size) {
14+
if (prices[i] > prices[i - 1]) {
15+
profit += prices[i] - prices[i - 1]
2616
}
27-
28-
day++
2917
}
30-
return totalProfit
31-
}
18+
return profit
19+
}
3220
```
3321

34-
> More simple way to implement.
22+
### Dynamic Programming
23+
We can apply the framework mentioned on [Dynamic Programming - Best Time to Buy and Sell Stock Problems](../topics/dynamic-programming.md#best-time-to-buy-and-sell-stock-problems), where `k` is positive infinite. (So we can simply ignore it)
3524

3625
```kotlin
3726
fun maxProfit(prices: IntArray): Int {
38-
var profit = 0
27+
val dp = Array(prices.size) { _ -> IntArray(2) }
28+
dp[0][0] = 0
29+
dp[0][1] = -prices[0]
3930
for (i in 1 until prices.size) {
40-
if (prices[i] > prices[i - 1]) {
41-
profit += prices[i] - prices[i - 1]
42-
}
31+
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i])
32+
// We can buy/sell more times, so we will have previous cash profit dp[i - 1][0], not 0, this is the difference of problem 121.
33+
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i])
4334
}
44-
return profit
35+
return dp[prices.size - 1][0]
4536
}
37+
```
38+
39+
```js
40+
Day i 1 2 3 4 5 6
41+
Price i 7 1 5 3 6 4
42+
----------------------------------
43+
dp[i][0] 0 0 4 4 7 7
44+
dp[i][1] -7 -1 -1 1 1 3
4645
```
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
## [123. Best Time to Buy and Sell Stock III](https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/)
2+
3+
The k times transaction limit
4+
5+
```kotlin
6+
fun maxProfit(prices: IntArray): Int {
7+
val maxK = 2
8+
val dp = Array(prices.size) { _ -> Array(2) { _ -> IntArray(maxK + 1) }}
9+
for (i in 0 until prices.size) {
10+
for (k in 1..maxK) {
11+
if (i == 0) {
12+
dp[i][0][k] = 0
13+
dp[i][1][k] = -prices[0]
14+
continue
15+
}
16+
dp[i][0][k] = max(dp[i - 1][0][k], dp[i - 1][1][k] + prices[i])
17+
// The max times transactions on (i - 1)-th day should be k - 1, so that we can buy on i-th day
18+
dp[i][1][k] = max(dp[i - 1][1][k], dp[i - 1][0][k - 1] - prices[i])
19+
}
20+
}
21+
return dp[prices.size - 1][0][maxK]
22+
}
23+
```

leetcode/1249.minimum-remove-to-make-valid-parentheses.md

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,38 @@
22

33
```kotlin
44
fun minRemoveToMakeValid(s: String): String {
5-
// The index of invalid right parenthese to be removed at first
6-
val toRmoveIndexList = mutableListOf<Int>()
7-
val leftParentheseStack = Stack<Int>()
5+
if (s.isEmpty()) return ""
6+
val toRemoveIndexes = hashSetOf<Int>()
7+
// left parentheses index
8+
val stack = Stack<Int>()
9+
// Scan the string to find invalid right parentheses
810
for (i in 0 until s.length) {
9-
if (s[i] == '(') {
10-
leftParentheseStack.push(i)
11-
} else if (s[i] == ')') {
12-
// The right parenthes can't be matched
13-
if (leftParentheseStack.isEmpty()) {
14-
toRmoveIndexList.add(i)
11+
val c = s[i]
12+
if (c == '(') {
13+
stack.push(i)
14+
} else if (c == ')') {
15+
if (stack.isEmpty()) {
16+
toRemoveIndexes.add(i)
1517
} else {
16-
leftParentheseStack.pop()
18+
stack.pop()
1719
}
1820
}
21+
}
22+
// Invalid left parentheses
23+
while (!stack.isEmpty()) {
24+
toRemoveIndexes.add(stack.pop())
1925
}
20-
while (!leftParentheseStack.isEmpty()) {
21-
toRmoveIndexList.add(leftParentheseStack.pop())
22-
}
23-
val result = StringBuilder()
26+
27+
val answer = StringBuilder()
2428
for (i in 0 until s.length) {
25-
if (toRmoveIndexList.contains(i)) continue
26-
else result.append(s[i])
29+
if (toRemoveIndexes.contains(i)) continue
30+
else answer.append(s[i])
2731
}
28-
return result.toString()
32+
return answer.toString()
2933
}
30-
```
34+
```
35+
36+
* **Time Complexity**: `O(n)`, to iterate the string for scanning and building the result.
37+
* **Space Complexity**: `O(n)` for hash table and stack.
38+
39+
> Another solution, we can iterate the string from left to right, to remove invalid right parentheses, then iterate from right to left to remove the invalid left parentheses.

leetcode/139.word-break.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
## [139. Word Break](https://leetcode.com/problems/word-break/)
2+
3+
Let's define `dp[i]` as the state if we can build the substring `s[0:i]` from diectionary. And the state transition will be
4+
5+
```js
6+
// The state of s[0:i]
7+
dp[i] =
8+
dp[j] // The state of s[0:j],
9+
&& s[j:i] // The remaining substring, check if it in the dictionary
10+
```
11+
12+
For example, `"cars"` and `["car", "ca", "rs"]`:
13+
14+
We will iterate `i` from 0 to `len` for find if we can build substring `s[0:i]`:
15+
16+
```js
17+
i j c a r s
18+
1, 0, c // dp[0] && dict.contains("c") = false
19+
2, 0, c a // dp[0] && dict.contains("ca") = true
20+
2, 1, a
21+
3, 0, c a r
22+
3, 1, a r
23+
3, 2, r
24+
4, 0, c a r s
25+
4, 1, a r s
26+
4, 2, r s
27+
4, 3, s
28+
```
29+
30+
```kotlin
31+
fun wordBreak(s: String, wordDict: List<String>): Boolean {
32+
val wordDictSet = HashSet(wordDict)
33+
val dp = BooleanArray(s.length + 1)
34+
35+
// Base case: it's always true for empty string case
36+
dp[0] = true
37+
38+
// Iterate every states
39+
for (i in 1..s.length) {
40+
for (j in 0 until i) {
41+
val substring = s.substring(j, i)
42+
// The state of s[0:j] and the state of s[j:i]
43+
if (dp[j] && wordDictSet.contains(substring)) {
44+
dp[i] = true
45+
break
46+
}
47+
}
48+
}
49+
return dp[s.length]
50+
}
51+
```
52+
53+
* **Time Complexity**: `O(n^2)` where `n` is the length of string.
54+
* **Space Complexity**: `O(n)` for dp table.
55+
56+
Here we don't iterate to build substring start from `i`, because the `dp[i]` represent the state of `s[0:i]`.
57+
58+
```js
59+
c a r s
60+
-------
61+
c
62+
c a
63+
c a r
64+
c a r s
65+
a
66+
a r
67+
a r s
68+
r
69+
r s
70+
s
71+
```
72+
73+
### My First Attempt (WA)
74+
To iterate all words in diectionary, and replace in the `s`, then check the remaining substring in diectionary recursively. It failed at the case `s = "cars"`, dictionary = `["car", "ca", "rs"]`.

0 commit comments

Comments
 (0)