题目描述
给定两个字符串 text1
和 text2
,返回他们的最长公共子序列的长度。
一个字符串的子序列是一个从原字符串中新生成的字符串,且该新字符串是原字符串删掉一些字符但不改变字符相对顺序生成的。
(例如,”ace” 是 “abcde” 的一个子序列,但 “aec” 不是)。两个字符串的公共子序列是一个对两个字符串都相同的子序列。
如果没有公共子序列,返回 0。
样例
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace",且长度为 3。
输入:text1 = "abc", text2 = "abc"
输出:
解释:最长公共子序列是 "abc",且长度为 3。
输入:text1 = "abc", text2 = "def"
输出:0
解释:没有这样的公共子序列,所以结果是 0。
限制
1 <= text1.length <= 1000
1 <= text2.length <= 1000
- 输入的字符串仅由小写英文字母组成。
算法
(动态规划) $O(nm)$
- 模板题。设 $f(i, j)$ 表示第一个字符串的前 $i$ 个字符和第二个字符串的前 $j$ 个字符的最长公共子序列。这里的字符串的下标假设从 1 开始。
- 初始时 $f(0, j) = f(i, 0) = 0$,表示空串不和任何字符串有公共子序列。
- 转移时,如果 $s1(i) == s2(j)$,则 $f(i, j) = \max(f(i - 1, j), f(i, j - 1), f(i - 1, j - 1) + 1)$,表示既可以忽视 $s1(i)$,也可以忽视 $s2(j)$,也可以让 $s1(i)$ 和 $s2(j)$ 匹配。
- 否则,$f(i, j) = \max(f(i - 1, j), f(i, j - 1)$,表示只能忽视 $s1(i)$ 或忽视 $s2(j)$。
- 最后答案为 $f(n, m)$。
时间复杂度
- 状态数为 $O(nm)$,转移为常数,故总时间复杂度为 $O(nm)$。
空间复杂度
- 空间复杂度和状态数一致,为 $O(nm)$。
C++ 代码
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int n = text1.length(), m = text2.length();
vector<vector<int>> f(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
if (text1[i - 1] == text2[j - 1])
f[i][j] = max(f[i - 1][j - 1] + 1,
max(f[i - 1][j], f[i][j - 1]));
else
f[i][j] = max(f[i - 1][j], f[i][j - 1]);
}
return f[n][m];
}
};
为什么这样也能过?
这样也是对的,相当于在字符匹配的情况下,从 $f(i - 1, j - 1) + 1$ 转移一定是最优的。在匹配权重为 $1$ 的情况下有这个性质,有兴趣可以证明下。
如果要输出最长公共子序列是什么?应该怎么记录下状态呢
可以从 f(n, m) 往前推,判断当前状态从哪来的
hhh,感谢,推出来啦
Java版