最稳妥的lcs二维dp解法是定义dpi为text1[0..i-1]与text2[0..j-1]的lcs长度,i、j从1开始,dp0和dp*初始化为0;字符相等时dpi=dpi-1+1,否则dpi=max(dpi-1, dpi);还原字符串需逆向回溯并反转结果。

用二维DP数组填表求LCS长度最稳妥
直接开 dp[i][j] 表示 text1[0..i-1] 和 text2[0..j-1] 的最长公共子序列长度,这是最不容易出错的写法。注意下标偏移:i、j 从 1 开始,dp[0][*] 和 dp[*][0] 全为 0。
状态转移只有两种情况:
- 如果
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])
别漏掉边界初始化——声明时用 vector<vector>> dp(m+1, vector<int>(n+1, 0))</int></vector>,比手写循环赋 0 更安全。
想还原LCS字符串就得回溯,不是只靠dp表
dp[m][n] 只给出长度,要得到实际子序列(比如 "ace"),必须从 dp[m][n] 往回走。回溯规则简单但容易写反:
- 若
text1[i-1] == text2[j-1],当前字符属于 LCS,加入结果,然后向dp[i-1][j-1]走 - 否则比较
dp[i-1][j]和dp[i][j-1]:哪个大就往哪走;相等时随便选一个(不影响长度,但影响具体解)
注意:回溯路径是逆序的,最后得反转字符串。别在回溯中直接 push_front 或用 deque,徒增复杂度。
空间优化到O(min(m,n))可行,但不能同时还原字符串
如果只需求长度,可以把二维 dp 压成一维:用 prev 记上一行,curr[j] 依赖 curr[j-1]、prev[j-1]、prev[j]。关键点是每次进内层循环前保存 prev[j-1](即左上角值),否则会被覆盖。
但一旦压缩空间,就丢失了完整路径信息,无法回溯构造 LCS 字符串。所以「省空间」和「要答案」二者只能选其一。
常见误操作:vector<int> dp(n+1)</int> 却仍按二维逻辑写 dp[i][j] —— 编译不过,或运行时越界。
LeetCode 1143 验证时注意输入为空的边界
测试用例包含空字符串,比如 text1 = "",此时无论 text2 是什么都应返回 0。DP 初始化已覆盖这点,但手写递归解法容易漏判 if (i == 0 || j == 0) return 0。
另一个坑是把 LCS 和最长公共子串(LCSubstring)混淆:子序列不要求连续,所以状态转移中不满足相等时,不能清零,而必须取两侧最大值。
真正难的不是推导,是在回溯时处理好下标映射和字符串拼接顺序——多调试两个短例(如 "abcbd" 和 "bdcaba"),比看十遍公式管用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











