题目:最长公共子序列(LCS)
给定两个字符串 text1 和 text2,求它们的 最长公共子序列(Longest Common Subsequence, LCS)的长度。
子序列 允许字符不连续,但必须按顺序出现。
1. 解题思路
本题采用 动态规划(DP) 解决。定义 dp[i][j] 为:
• dp[i][j]:表示 text1[0:i] 和 text2[0:j] 的最长公共子序列长度。
2. 状态转移方程
遍历 text1 和 text2:
• 如果当前字符相等:
• dp[i][j] = dp[i - 1][j - 1] + 1
• 说明当前两个字符可以加入 LCS,因此长度增加 1。
• 如果当前字符不相等:
• dp[i][j] = max(dp[i - 1][j], dp[i][j - 1])
• 说明必须舍弃 text1[i-1] 或 text2[j-1] 之一,取较大的子问题结果。
边界条件
• dp[0][j] = 0(当 text1 为空时,LCS 长度为 0)。
• dp[i][0] = 0(当 text2 为空时,LCS 长度为 0)。
3. 代码解析
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int m = text1.length(), n = text2.length();
vector<vector<int>> dp(m + 1, vector<int>(n + 1)); // 定义 dp 数组
for (int i = 1; i <= m; i++) { // 遍历 text1
char c1 = text1[i - 1]; // 取当前字符
for (int j = 1; j <= n; j++) { // 遍历 text2
char c2 = text2[j - 1]; // 取当前字符
if (c1 == c2) { // 相等时,LCS 增长
dp[i][j] = dp[i - 1][j - 1] + 1;
} else { // 不相等时,取之前的最大值
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n]; // 返回最终 LCS 长度
}
};
4. 具体运行步骤
示例 1
输入:
text1 = "abcde"
text2 = "ace"
dp 数组计算过程:
text1 \ text2 | “” | a | c | e |
---|---|---|---|---|
“” | 0 | 0 | 0 | 0 |
a | 0 | 1 | 1 | 1 |
b | 0 | 1 | 1 | 1 |
c | 0 | 1 | 2 | 2 |
d | 0 | 1 | 2 | 2 |
e | 0 | 1 | 2 | 3 |
输出:3 (LCS 是 "ace")
示例 2
输入:
text1 = "abc"
text2 = "def"
dp 数组计算过程:
text1 \ text2 | “” | d | e | f |
---|---|---|---|---|
“” | 0 | 0 | 0 | 0 |
a | 0 | 0 | 0 | 0 |
b | 0 | 0 | 0 | 0 |
c | 0 | 0 | 0 | 0 |
输出:0(没有公共子序列)
5. 复杂度分析
• 时间复杂度:O(m × n),m 和 n 分别是 text1 和 text2 的长度。
• 空间复杂度:O(m × n),用于存储 dp 数组。可以优化为 O(n) 使用 滚动数组。
6. 优化方案(O(n) 空间复杂度)
我们可以用 滚动数组 优化空间复杂度,只存储前一行 dp 值:
class Solution {
public:
int longestCommonSubsequence(string text1, string text2) {
int m = text1.length(), n = text2.length();
vector<int> prev(n + 1, 0), curr(n + 1, 0);
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (text1[i - 1] == text2[j - 1]) {
curr[j] = prev[j - 1] + 1;
} else {
curr[j] = max(prev[j], curr[j - 1]);
}
}
swap(prev, curr); // 旧行变成新行
}
return prev[n];
}
};
空间复杂度优化至 O(n) 🎯
7. 总结
✅ 核心思想:
1. 用 dp[i][j] 记录 text1[0:i] 和 text2[0:j] 的 LCS 长度。
2. 状态转移:
• 若 text1[i-1] == text2[j-1],则 dp[i][j] = dp[i-1][j-1] + 1。
• 否则 dp[i][j] = max(dp[i-1][j], dp[i][j-1])。
3. 通过 滚动数组 优化 空间复杂度 至 O(n)。