Update subsequence
All checks were successful
ci/woodpecker/push/test Pipeline was successful

This commit is contained in:
Sainnhe Park 2023-02-10 17:00:23 +08:00
parent ed62e1dd71
commit 7e43421a71

View File

@ -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/) ## [300. 最长递增子序列](https://leetcode.cn/problems/longest-increasing-subsequence/)
- `dp[i]` 表示以 `nums[i]` 结尾的最长递增子序列的长度 - `dp[i]` 表示以 `nums[i]` 结尾的最长递增子序列的长度
@ -31,8 +39,88 @@
- `dp[i][j]` 表示以 `i` 为下标结尾的 text1 和以 `j` 为下标结尾的 text2 的最长公共子序列 - `dp[i][j]` 表示以 `i` 为下标结尾的 text1 和以 `j` 为下标结尾的 text2 的最长公共子序列
- 递推公式 - 递推公式
- `if (text1[i] == text2[j]) dp[i][j] = dp[i - 1][j - 1] + 1` - `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[0][0]`
- `dp[1][0]` - `dp[1][0]`
- `dp[0][1]` - `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`