LeetCode 5. 最长回文子串
题目描述

题意分析
在字符串中找出最长的一段连续字符,使它从左往右读和从右往左读完全相同,返回这段子串本身。
子串必须连续,不能跳过字符拼成回文。回文的长度可以是奇数,也可以是偶数;如果有多个同样长的最长回文子串,返回任意一个即可。题目保证输入非空,单个字符也算回文。
解法:中心扩展
核心思路
[!blue]
回文的两侧字符关于中心对称。如果中间一段已经是回文,并且它左右紧邻的字符相同,那么把这两个字符一起纳入后仍然是回文。因此可以从中心出发,不断向两侧扩展,直到越界或遇到不相等的字符。
奇数长度回文的中心是一个字符,从
(i, i)开始比较;偶数长度回文的中心在两个相邻字符之间,从(i, i + 1)开始比较。每个回文都有这两类中心之一,枚举所有位置并检查两类中心,就不会漏掉最长回文。扩展时左右指针同步向外移动。停止时,两端已经不属于有效回文,真正的区间是
[left + 1, right - 1],长度为right - left - 1。每次只保留更长区间的边界,最后再截取字符串,避免反复构造子串。Java 的扩展函数返回长度,需要从中心
i还原边界。奇数长度时左右跨度相同;偶数长度时i是中间两个字符的左边那个,右侧比左侧多占一位。因此左边界统一为i - (length - 1) / 2,右边界统一为i + length / 2,这里的除法都向下取整。Go 直接返回有效区间的左右端点,不需要再换算。
解题步骤
- 将答案初始化为首字符,枚举中心位置
i。- 分别从
(i, i)和(i, i + 1)向两侧扩展;下标有效且字符相等时,左指针减一、右指针加一。- 扩展停止后向内退一格,得到当前回文区间;若它更长,就更新答案边界。
- Java 根据回文长度换算边界:
start = i - (length - 1) / 2、end = i + length / 2;Go 直接使用扩展返回的边界。- 返回从
start到end的子串,截取时右边界使用end + 1。
代码实现
class Solution {
public String longestPalindrome(String s) {
int start = 0;
int end = 0;
for (int i = 0; i < s.length(); i++) {
// 同时检查单字符中心和两字符之间的中心。
int length = Math.max(expand(s, i, i), expand(s, i, i + 1));
if (length > end - start + 1) {
// 将奇偶中心统一换算成左右端点,长度减一后再计算左半跨度。
start = i - (length - 1) / 2;
end = i + length / 2;
}
}
return s.substring(start, end + 1);
}
private int expand(String s, int left, int right) {
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
left--;
right++;
}
// 两端已越过有效回文,区间长度要去掉越出的两端。
return right - left - 1;
}
}
func longestPalindrome(s string) string {
start, end := 0, 0
expand := func(left, right int) (int, int) {
for left >= 0 && right < len(s) && s[left] == s[right] {
left--
right++
}
// 停止时两端已越界或不等,向内退一格才是有效区间。
return left + 1, right - 1
}
for i := 0; i < len(s); i++ {
// 同时检查单字符中心和两字符之间的中心。
for _, center := range [][2]int{
{i, i},
{i, i + 1},
} {
left, right := expand(center[0], center[1])
if right-left > end-start {
start, end = left, right
}
}
}
return s[start : end+1]
}
复杂度分析
- 时间复杂度:$O(n^2)$,有 $O(n)$ 个中心,每个中心最多扩展 $O(n)$ 次。
- 空间复杂度:$O(1)$,只维护扩展指针和答案边界,不计返回结果。
关键点总结
[!green]
- 奇数和偶数长度回文需要使用两类中心。
- 扩展停止后指针已越过合法区间一位。
- 中心扩展比区间 DP 少用 $O(n^2)$ 空间。
易错点总结
[!yellow]
- 只检查
(i, i),会漏掉偶数长度回文。- 截取字符串时混淆闭区间与左闭右开区间。
- 空串时下标会越界;本题约束保证字符串非空。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 647. 回文子串 | 中等 | 同样按中心扩展回文区间,原题统计全部回文子串,本题只保留最长区间。 |
| 516. 最长回文子序列 | 中等 | 原题允许跳过字符形成回文子序列,本题要求连续,不能直接套相同状态。 |
| 补充题 216. 最长回文子串的长度 | 中等 | 都围绕单个或两个中心向两侧扩展;本题返回子串,补充题只返回长度。 |
| 131. 分割回文串 | 中等 | 用区间或中心扩展刻画回文结构;本题寻找最长连续回文,该题预处理回文后枚举切分方案。 |
| 132. 分割回文串 II | 困难 | 用区间或中心扩展刻画回文结构;本题寻找最长连续回文,该题在回文区间上递推最少切割次数。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!