【LeetCode.1143】最长公共子序列

题目: 最长公共子序列

​ 给定两个字符串 text1text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0

​ 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

​ 例如,"ace""abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。
​ 两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例 1:

1
2
3
输入:text1 = "abcde", text2 = "ace" 
输出:3
解释:最长公共子序列是 "ace" ,它的长度为 3 。

示例 2:

1
2
3
输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。

示例 3:

1
2
3
输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。

提示:

1 <= text1.length, text2.length <= 1000
text1 和 text2 仅由小写英文字符组成。


分析:

定义 dp[i][j] 表示 text1[0:i]text2[0:j] 的最长公共子序列,其中 text1[0:i] 表示数组中从0i长度为 i + 1的子数组

text1[i - 1] == text2[j - 1] 时,说明两个子字符串的最后一位相等,所以最长公共子序列又增加了 1,所以 dp[i][j] = dp[i - 1][j - 1] + 1;举个例子,比如对于 acbc 而言,他们的最长公共子序列的长度等于 ab 的最长公共子序列长度 0 + 1 = 1
text1[i - 1] != text2[j - 1] 时,说明两个子字符串的最后一位不相等,那么此时的状态 dp[i][j] 应该是 dp[i - 1][j]dp[i][j - 1] 的最大值。举个例子,比如对于 acebc 而言,他们的最长公共子序列的长度等于 ① aceb 的最长公共子序列长度0 与 ② acbc 的最长公共子序列长度1 的最大值,即 1

综上状态转移方程为:

dp[i][j] = dp[i - 1][j - 1] + 1, 当 text1[i - 1] == text2[j - 1];text1[i−1]==text2[j−1];
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]), 当 text1[i - 1] != text2[j - 1]text1[i−1]!=text2[j−1]


题解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Solution {
public:
//动态规划
int longestCommonSubsequence(string text1, string text2) {
int n = text1.size(), m = text2.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1));
//逐渐将text1[i - 1]和text2[j - 1]纳入考虑,更新dp数组
for(int i = 1; i <= n; i++){
char c1 = text1[i - 1];
for(int j = 1; j <= m; j++){
char c2 = text2[j - 1];
if(c1 == c2) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i][j - 1], dp[i - 1][j]);
}
}
return dp[n][m];
}
};

总结:

遇到考查动态规划的题也算有点头绪了,我的感悟是这是一种从无到有的思想:一步一步将单个信息纳入考虑范围,然后按照条件更新已知的情报。其中的难点在于dp数组的定义,怎么样的定义才能有效快捷地达成目的呢?