5.2 KiB
5.2 KiB
子序列问题
子序列问题一般涉及到两个序列 s
和 t
,dp[i][j]
一般设计成 i
和 j
为这两个序列的索引。
我们需要讨论的是当 s[i] == t[j]
和 s[i] != t[j]
时,如何由以下三个推导出 dp[i][j]
:
dp[i - 1][j - 1]
dp[i - 1][j]
dp[i][j - 1]
300. 最长递增子序列
dp[i]
表示以nums[i]
结尾的最长递增子序列的长度- 遍历
j
从0
到i
,if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1)
dp[i] = 1
- 外层遍历
i
内层遍历j
,都是从前往后
674. 最长连续递增序列
dp[i]
表示以nums[i]
结尾的最长连续递增子序列的长度- 递推公式:
if (nums[i] > nums[i - 1]) dp[i] = dp[i - 1] + 1
if (nums[i] <= nums[i - 1]) dp[i] = 1
dp[0] = 1
- 从前往后
718. 最长重复子数组
dp[i][j]
表示以i
为下标结尾的 A 和以j
为下标结尾的 B 的最长重复子数组if (nums1[i] == nums2[j]) dp[i][j] = dp[i - 1][j - 1] + 1
否则为0
- 初始化:
if (nums1[i] == nums2[0]) dp[i][0] = 1
否则为0
if (nums1[0] == nums2[j]) dp[0][j] = 1
否则为0
- 其它都初始化为
0
- 都从前往后遍历,下标从
1
开始
1143. 最长公共子序列
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] = max(dp[i - 1][j], dp[i][j - 1])
- 只需要初始化三个:
dp[0][0]
dp[1][0]
dp[0][1]
53. 最大子序和
dp[i]
为包括下标i
的最大连续子序列和max(dp[i - 1] + nums[i], nums[i])
dp[0] = nums[0]
- 从前往后
115. 不同的子序列
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. 两个字符串的删除操作
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. 编辑距离
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. 回文子串
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. 最长回文子序列
字串要连续,子序列不用连续。
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