This commit is contained in:
parent
ed62e1dd71
commit
7e43421a71
@ -1,5 +1,13 @@
|
||||
# 子序列问题
|
||||
|
||||
子序列问题一般涉及到两个序列 `s` 和 `t`,`dp[i][j]` 一般设计成 `i` 和 `j` 为这两个序列的索引。
|
||||
|
||||
我们需要讨论的是当 `s[i] == t[j]` 和 `s[i] != t[j]` 时,如何由以下三个推导出 `dp[i][j]`:
|
||||
|
||||
1. `dp[i - 1][j - 1]`
|
||||
2. `dp[i - 1][j]`
|
||||
3. `dp[i][j - 1]`
|
||||
|
||||
## [300. 最长递增子序列](https://leetcode.cn/problems/longest-increasing-subsequence/)
|
||||
|
||||
- `dp[i]` 表示以 `nums[i]` 结尾的最长递增子序列的长度
|
||||
@ -31,8 +39,88 @@
|
||||
- `dp[i][j]` 表示以 `i` 为下标结尾的 text1 和以 `j` 为下标结尾的 text2 的最长公共子序列
|
||||
- 递推公式
|
||||
- `if (text1[i] == text2[j]) dp[i][j] = dp[i - 1][j - 1] + 1`
|
||||
- `else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])`
|
||||
- `if (text1[i] != text2[j]) dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])`
|
||||
- 只需要初始化三个:
|
||||
- `dp[0][0]`
|
||||
- `dp[1][0]`
|
||||
- `dp[0][1]`
|
||||
|
||||
## [53. 最大子序和](https://leetcode.cn/problems/maximum-subarray/)
|
||||
|
||||
- `dp[i]` 为包括下标 `i` 的最大连续子序列和
|
||||
- `max(dp[i - 1] + nums[i], nums[i])`
|
||||
- `dp[0] = nums[0]`
|
||||
- 从前往后
|
||||
|
||||
## [115. 不同的子序列](https://leetcode.cn/problems/distinct-subsequences/)
|
||||
|
||||
- `dp[i][j]` 表示在 `s[...i]` 中 `t[...j]` 出现的的次数
|
||||
- 递推公式:
|
||||
- `if (s[i] == t[j])`
|
||||
- 第一种组合方式是在 `s[...i-1]` 中 `t[...j-1]` 出现了 `dp[i - 1][j - 1]` 次,这时候我们把末尾的 `s[i]` 和 `t[j]` 分别加上去,之前的每一次匹配依然有效,所以出现次数为 `dp[i - 1][j - 1]`
|
||||
- 第二种组合方式是,`t[...j]` 有可能本身就在 `s[...i-1]` 中出现过,出现的次数是 `dp[i - 1][j]`
|
||||
- 把这两种情况的数量加起来就是 `dp[i][j]` 了:`dp[i][j] = dp[i - 1][j - 1] + dp[i - 1][j]`
|
||||
- `if (s[i] != t[j])`
|
||||
- 只有一种可能,那就是 `t[...j]` 本身就在 `s[...i-1]` 中出现过,出现次数为 `dp[i - 1][j]`
|
||||
- 需要初始化两个:
|
||||
- `dp[i][0]`
|
||||
- `dp[0][j]`
|
||||
- 都从前往后遍历
|
||||
|
||||
## [583. 两个字符串的删除操作](https://leetcode.cn/problems/delete-operation-for-two-strings/)
|
||||
|
||||
- `dp[i][j]` 表示 `word1[...i]` 和 `word2[...j]` 所需的操作数
|
||||
- 递推公式:
|
||||
- `if (word1[i] == word2[j]) dp[i][j] = dp[i - 1][j - 1]`
|
||||
- `if (word1[i] == word2[j]) dp[i][j] = min(dp[i - 1][j] + 1, dp[i][j - 1] + 1)`
|
||||
- 初始化
|
||||
- `dp[i][0] = i`
|
||||
- `dp[0][j] = j`
|
||||
- 从前往后遍历
|
||||
|
||||
## [72. 编辑距离](https://leetcode.cn/problems/edit-distance/)
|
||||
|
||||
- `dp[i][j]` 是把 `word1[...i]` 转换成 `word2[...j]` 所需的最少操作数
|
||||
- 递推公式:
|
||||
- `if (word1[i] == word2[j]) dp[i][j] = dp[i - 1][j - 1]`
|
||||
- `if (word1[i] != word2[j])`
|
||||
- 从 `dp[i - 1][j - 1]` 转换过去,需要把 `word1[i]` 替换成 `word2[j]`,也就是 `dp[i - 1][j - 1] + 1` 步操作
|
||||
- 从 `dp[i][j - 1]` 转换过去,需要在 `word1[...i]` 的末尾插入一个 `word2[j]`,也就是 `dp[i][j - 1] + 1` 步操作
|
||||
- 从 `dp[i - 1][j]` 转换过去,需要删除 `word1[i]`,也就是 `dp[i - 1][j] + 1` 步操作
|
||||
- 这三种情况取最小,即 `min(dp[i - 1][j - 1] + 1, dp[i][j - 1] + 1, dp[i - 1][j] + 1)`
|
||||
- 初始化:
|
||||
- `dp[i][0]`
|
||||
- `dp[0][j]`
|
||||
- 从前向后遍历
|
||||
|
||||
## [647. 回文子串](https://leetcode.cn/problems/palindromic-substrings/)
|
||||
|
||||
- `dp[i][j]` 表示 `s[i...j]` 是否是回文串
|
||||
- 递推公式:
|
||||
- `if (s[i] == s[j])`
|
||||
- `if (i == j) dp[i][j] = true`
|
||||
- `else if (j = i + 1) dp[i][j] = true`
|
||||
- `else dp[i][j] = dp[i + 1][j - 1]`
|
||||
- `if (s[i] != s[j]) dp[i][j] = false`
|
||||
- 全部初始化为 `false`
|
||||
- 遍历顺序:
|
||||
- `i` 从后往前,范围是 `0...len-1`
|
||||
- `j` 从前往后,范围是 `i...len-1`
|
||||
|
||||
## [516. 最长回文子序列](https://leetcode.cn/problems/longest-palindromic-subsequence/)
|
||||
|
||||
字串要连续,子序列不用连续。
|
||||
|
||||
- `dp[i][j]` 表示 `s[i...j]` 的最长回文子序列的长度
|
||||
- 递推公式:
|
||||
- `if (s[i] != s[j])`
|
||||
- `if (j = i + 1) dp[i][j] = 0`
|
||||
- `else dp[i][j] = dp[i + 1][j - 1]`
|
||||
- `if (s[i] == s[j])`
|
||||
- `if (i == j) dp[i][j] = 1`
|
||||
- `else if (j = i + 1) dp[i][j] = 2`
|
||||
- `else dp[i][j] = dp[i + 1][j - 1] + 2`
|
||||
- 全部初始化为 `0`
|
||||
- 遍历顺序:
|
||||
- `i` 从后往前,范围是 `0...len-1`
|
||||
- `j` 从前往后,范围是 `i...len-1`
|
||||
|
Loading…
Reference in New Issue
Block a user