两道字符串经典题复盘:最长回文子串 & 最长公共子序列

今天复盘两道面试高频的字符串动态规划题:「最长回文子串」「最长公共子序列」。一道是字符串回文问题的基础,另一道是双字符串 DP 的经典模板,两者都是字符串处理的核心考点。


一、5. 最长回文子串(中等)

题目描述

给你一个字符串 s,找到 s 中最长的回文子串。

核心思路 1:中心扩展法(直观高效)

回文的本质是对称,所以我们可以把每个字符(奇数长度)和每对相邻字符(偶数长度)当作中心,向两边扩展,找到以该中心为对称轴的最长回文子串。

  • 对于每个中心 i
    • 奇数长度:以 (i, i) 为中心扩展
    • 偶数长度:以 (i, i+1) 为中心扩展
  • 每次扩展时,只要左右字符相等就继续,直到不相等为止,记录当前最长的子串。

完整代码(Java)

java

运行

复制代码
class Solution {
    public String longestPalindrome(String s) {
        if (s == null || s.length() < 1) return "";
        int start = 0, end = 0;
        for (int i = 0; i < s.length(); i++) {
            int len1 = expandAroundCenter(s, i, i);
            int len2 = expandAroundCenter(s, i, i + 1);
            int len = Math.max(len1, len2);
            if (len > end - start) {
                start = i - (len - 1) / 2;
                end = i + len / 2;
            }
        }
        return s.substring(start, end + 1);
    }

    private int expandAroundCenter(String s, int left, int right) {
        while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
            left--;
            right++;
        }
        return right - left - 1;
    }
}

核心思路 2:动态规划

定义 dp[i][j] 表示字符串 s[i..j] 是否为回文子串:

  • 状态转移:dp[i][j] = (s[i] == s[j]) && dp[i+1][j-1]
  • 边界条件:
    • 单个字符:dp[i][i] = true
    • 两个字符:dp[i][i+1] = (s[i] == s[i+1])
  • 遍历方式:按子串长度从小到大遍历,避免依赖未计算的状态。

时间与空间复杂度

  • 中心扩展法:时间 O (n²),空间 O (1)
  • 动态规划:时间 O (n²),空间 O (n²)(可优化为一维)

二、1143. 最长公共子序列(中等)

题目描述

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

核心思路:二维动态规划

这是双字符串 DP 的经典模板题,核心是定义状态并利用 "选或不选" 的思想转移:

  • 定义 dp[i][j] 表示 text1[0..i-1]text2[0..j-1] 的最长公共子序列长度。
  • 状态转移:
    • 如果 text1[i-1] == text2[j-1]dp[i][j] = dp[i-1][j-1] + 1(当前字符匹配,长度 + 1)
    • 如果不相等:dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1])(选其中一个字符串的前一个状态)
  • 边界条件:dp[0][j] = 0dp[i][0] = 0(空字符串的公共子序列长度为 0)

完整代码(Java)

java

运行

复制代码
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

因为每次更新 dp[i][j] 只需要上一行的 dp[i-1][j] 和当前行的 dp[i][j-1],可以将二维数组压缩为一维,空间复杂度从 O (mn) 降到 O (min (m,n)):

java

运行

复制代码
class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        if (text1.length() < text2.length()) {
            return longestCommonSubsequence(text2, text1);
        }
        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 中心扩展 / DP 状态转移 O(n²) O (1)(中心扩展) 回文对称性、子串与子序列的区别
最长公共子序列 双字符串 DP 选或不选的状态转移 O(mn) O (min (m,n))(优
相关推荐
九成宫1 天前
WSL2 网络优化配置:提升Git克隆与包下载速度
windows·笔记·代理模式·pip·wsl
吴声子夜歌2 天前
Java——动态代理
java·开发语言·代理模式
蜡笔小马3 天前
10.C++设计模式-代理模式
c++·设计模式·代理模式
BUG制造者:图图4 天前
MiMo 模型 Tool Calls 400 报错终极解决方案——Reasoning Content 代理中间件
中间件·代理模式·mimo·小米模型
雪度娃娃5 天前
结构型设计模式——代理模式
java·c++·设计模式·系统安全·代理模式
庞轩px8 天前
第六篇:Spring用了哪些设计模式?——从单例到代理,拆解框架中的经典设计
java·spring·设计模式·bean·代理模式·aop·单例
多加点辣也没关系8 天前
数据结构与算法|第二十四章:算法思维总结与实战
算法·代理模式
c++之路8 天前
代理模式(Proxy Pattern)
开发语言·c++·代理模式
qq_三哥啊14 天前
【mitmproxy】通过 mitmproxy 的HTTP代理模式获取 OpenCode 发起的 AI API 请求的详细信息
网络·http·代理模式