从“看起来像双指针”到真正的动态规划 —— 最长公共子序列

一、题目速览

LeetCode 1143. 最长公共子序列(Medium)

给定两个字符串 text1text2,返回它们的 最长公共子序列的长度

若不存在公共子序列,返回 0

示例

复制代码
text1 = "abcde"
text2 = "ace"

输出:3
解释:最长公共子序列是 "ace",长度为 3。

二、第一反应为什么容易错?

很多人第一眼会想:

"能不能用双指针?"

不行,因为:

  • 子序列 不要求连续

  • 双指针只能处理"连续匹配"

  • 一旦跳字符,顺序就乱了


三、为什么一定是动态规划?

关键特征全部命中:

DP 特征 本题表现
有重叠子问题 同一个前缀对会被反复计算
有最优子结构 整体最优由局部最优构成
无后效性 后面的选择不影响前面的结果

👉 LCS 是 DP 教科书级例题


四、状态设计(最重要的一步)

定义状态

复制代码
dp[i][j] = text1 前 i 个字符 与 text2 前 j 个字符 的最长公共子序列长度

注意:

ij表示 前缀长度,不是下标


五、状态转移(核心逻辑)

情况 1:当前字符相同

复制代码
text1[i-1] == text2[j-1]
dp[i][j] = dp[i-1][j-1] + 1

✅ 把这个字符加入公共子序列


情况 2:当前字符不同

复制代码
text1[i-1] != text2[j-1]
dp[i][j] = max(dp[i-1][j], dp[i][j-1])

✅ 至少丢掉其中一个字符,取更优的结果


合并写法

复制代码
if text1[i-1] == text2[j-1]:
    dp[i][j] = dp[i-1][j-1] + 1
else:
    dp[i][j] = max(dp[i-1][j], dp[i][j-1])

六、基础边界

复制代码
dp[0][*] = 0
dp[*][0] = 0

空字符串与任何字符串的公共子序列长度都是 0。


七、执行过程示例

text1 = "abcde"

text2 = "ace"

DP 表(简化):

Ø 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 ✅**​

八、Java 实现(面试版)

二维 DP(最清晰)

复制代码
class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int m = text1.length();
        int n = text2.length();
        int[][] dp = new int[m + 1][n + 1];

        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[m][n];
    }
}

九、空间优化(进阶加分)

因为 dp[i][j]只依赖:

  • 上一行

  • 当前行前一个

👉 可用一维数组优化:

复制代码
class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int m = text1.length();
        int n = text2.length();
        int[] dp = new int[n + 1];

        for (int i = 1; i <= m; i++) {
            int prev = 0;
            for (int j = 1; j <= n; j++) {
                int temp = dp[j];
                if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
                    dp[j] = prev + 1;
                } else {
                    dp[j] = Math.max(dp[j], dp[j - 1]);
                }
                prev = temp;
            }
        }
        return dp[n];
    }
}

十、复杂度分析

版本 时间复杂度 空间复杂度
二维 DP O(m × n) O(m × n)
一维 DP O(m × n) O(n)

十一、面试答题模板(直接背)

"这是一道经典的二维动态规划问题。

我们用 dp[i][j]表示 text1i个字符和 text2j个字符的最长公共子序列长度。

如果两个字符相同,就继承左上角的值并加一;

如果不同,就取上方和左方的最大值。

时间复杂度 O(m×n),空间复杂度可以优化到 O(n)。"


十二、一句话总结

**LCS 的本质不是"找字符串",而是"数状态"。**​

你不是在比较两个字符串,而是在比较它们所有可能的"前缀对"。

相关推荐
05候补工程师1 小时前
【考研高数核心突破】极限的本质、高频解题套路与海涅定理深度解析(附经典例题思维导图式拆解)
经验分享·笔记·考研·算法
智者知已应修善业1 小时前
【51单片机8个LED的花样12亮34熄56间隔78闪烁3秒3闪烁】2023-11-4
c++·经验分享·笔记·算法·51单片机
老鱼说AI2 小时前
统计学习方法第五章:从浅入深解析决策树
人工智能·深度学习·算法·决策树·机器学习·学习方法
KaMeidebaby2 小时前
卡梅德生物技术快报|蛋白修饰调控 NETosis 分子机制及实验研究进展
前端·数据库·人工智能·算法·百度
初中就开始混世的大魔王2 小时前
5 Fast DDS-Discovery
网络·c++·算法·中间件
Deep-w2 小时前
【MATLAB】基于模型预测控制的自适应巡航车辆过渡工况安全控制研究
开发语言·人工智能·算法·机器学习·matlab
运行时记录2 小时前
Sirchmunk 让搜索随查询自进化
算法
浮生望2 小时前
双指针算法面试通关指南:从入门到精通
算法
SimpleLearingAI2 小时前
PyTorch & Numpy 实现线性回归详解
人工智能·算法·多模态大模型