LeetCode 5:最长回文子串(Longest Palindromic Substring)题解
🔗 题目链接
👉 https://leetcode.cn/problems/longest-palindromic-substring/
📖 题目描述
给你一个字符串 s,找出其中最长的连续回文子串。若存在多个长度相同的最长回文子串,返回任意一个即可。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
题目约束:
1 <= s.length <= 1000- 字符串仅由数字和英文字母组成
📌 题目核心考点
- 经典 区间动态规划(区间DP) 题型
- 字符串子串问题、回文判定进阶应用
- 面试高频算法原题,重点考察DP状态定义、遍历顺序逻辑
💡 解题核心思路(DP解法)
暴力枚举所有子串再判断回文的方式时间复杂度为 O(n³),会超时。因此使用动态规划,复用已计算的子问题结果,将时间复杂度优化至 O(n²)。
一、状态定义
定义二维DP数组:
dp[i][j] = 表示字符串区间 s[i...j] 是否为回文子串
true:是回文子串,false:不是回文子串
二、状态转移方程(核心)
回文子串的核心特性:两端字符相等,且中间包裹的子串也是回文。
if (s[i] == s[j]) {
// 情况1:单个字符 / 两个相邻相同字符,一定是回文
if (j - i <= 1) {
dp[i][j] = true;
}
// 情况2:区间长度大于2,依赖内部子串的结果
else {
dp[i][j] = dp[i + 1][j - 1];
}
}
简单总结:首尾字符相同,且内部子串是回文,则当前区间是回文。
三、关键遍历顺序(易错重点)
dp[i][j] 的状态依赖于 dp[i+1][j-1],因此遍历顺序固定:
- i(左边界):从后往前遍历
- j(右边界):从前往后遍历
该顺序保证:计算大区间时,内部小区间的子问题已经算完。
for (int i = len - 1; i >= 0; i--)
for (int j = i; j < len; j++)
✅ 完整AC代码(修缮你的原代码)
java
class Solution {
public String longestPalindrome(String s) {
// 初始最长长度为1,答案初始化为第一个字符(修复原代码空串BUG)
int maxLen = 1;
String ans = s.substring(0, 1);
int len = s.length();
char[] charArr = s.toCharArray();
boolean[][] dp = new boolean[len][len];
// 左边界倒序遍历
for (int i = len - 1; i >= 0; i--) {
// 右边界正序遍历
for (int j = i; j < len; j++) {
if (charArr[i] == charArr[j]) {
// 长度1、2直接回文
if (j - i <= 1) {
dp[i][j] = true;
} else {
// 长度大于2依赖内部
dp[i][j] = dp[i + 1][j - 1];
}
// 更新最长回文子串
if (dp[i][j] && (j - i + 1) > maxLen) {
maxLen = j - i + 1;
ans = s.substring(i, j + 1);
}
}
}
}
return ans;
}
}
⏱️ 复杂度分析
| 指标 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n²) | 两层循环枚举所有区间 |
| 空间复杂度 | O(n²) | 二维dp数组开销 |
💡 核心一句话总结
区间DP:首尾相等且内部子串回文 → 当前区间回文;逆序遍历左边界,保证子问题先解。
📝 面试高频追问(必背)
1. 为什么 i 必须倒序遍历?
dp[i][j] 依赖 dp[i+1][j-1],也就是当前行依赖下一行 。
只有 i 从后往前算,i+1 的结果才已经提前算好了。
2. 为什么要单独判断 j - i <= 1?
当子串长度为 1、2 时,i+1 > j-1,没有内部子区间,不能走转移方程,需要手动初始化基准回文。
3. 可以优化空间吗?
可以。中心扩展法空间 O(1),时间同样 O(n²),是面试最优解。
需要我把中心扩展法的完整题解+代码也直接贴给你吗?