文章目录
1143.最长公共子序列(注意递推的逻辑)
- 体会一下本题和 718. 最长重复子数组 的区别,主要是本题不连续,不连续的递推逻辑一定要注意
给定两个字符串 text1
和 text2
,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0
。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
- 例如,
"ace"
是"abcde"
的子序列,但"aec"
不是"abcde"
的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。
示例 1:
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace" ,它的长度为 3 。
示例 2:
输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。
示例 3:
输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。
提示:
1 <= text1.length, text2.length <= 1000
text1
和text2
仅由小写英文字符组成。
思路
本题与 718.最长重复子数组 的区别就在于,本题不要求两个数组的重复元素连续了,但是需要保持元素的相对顺序。
如果不需要保持元素连续,那么一个重要区别就是,字符串包含的数字越多,两个数组的重复元素存在的可能性越高,包含数字多的字符串重复元素数量一定>=包含数字少的
DP数组含义
这类需要两个序列挨个对比的题目,DP数组含义最好都设置为:长度为[0,i-1]的字符串1和长度为[0,j-1]的字符串2,两个字符串的公共子序列长度为dp[i][j]
,注意并不是最长,因为最长是单独用result变量去筛选的。
这样定义是为了后面代码实现方便,其实就是简化了dp数组第一行和第一列的初始化逻辑。详见上一题的博客。
递推公式
本题的递推比较重要,因为要求不连续的重复子序列长度,如果当前下标对应的数字相等,那么递推公式和上一题相同;如果当前下标对应的数字不等,也就是存在错位的情况,例如下图所示的例子:
(注意为了方便理解这里dp[i][j]
直接写成了对应nums[i]和nums[j])
像上图这个例子,我们可以看到下标i=4和下标j=0的情况是重复的,这种错位较大的重复情况,也可以通过对dp[i-1][j]
和dp[i][j-1]
进行传递得到!
也就是说,如果当前遍历到的下标不相等的话,可以通过传递dp[i-1][j]
和dp[i][j-1]
,获取之前错位的部分,相等元素的累积值!
完整的递推公式:
int result=0;
for(int i=1;i<=nums.size();i++){
for(int j=1;j<=nums.size();j++){
if(nums[i-1]==nums[j-1]){
//如果相等长度++
dp[i][j]=dp[i-1][j-1]+1;
}
else{
//如果不相等,直接调取两个错位的累积值
dp[i][j]=max(dp[i-1][j],dp[i],[j-1]);
}
result=max(result,dp[i][j]);
}
}
初始化
因为本题DP数组的含义依旧是**dp[i][j]
代表以下标nums[i-1]数字和下标nums[j-1]数字结尾的字符串最大公共序列长度**,因此初始化全部为0即可,dp[0][j]
和dp[i][0]
都是0。
完整版
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
//全部初始化为0
vector<vector<int>>dp(text1