5. 最长回文子串(中等)
Blind 75 › 5. 最长回文子串(中等) (第 4 章)
题目
给定一个字符串 s,找出其中最长的回文子串——也就是连续的一段、正着读反着读都一样。
若存在多个同样长的答案,返回任意一个即可。
自造示例:"racecars" → "racecar"(奇数长度,以中间的 e 为对称中心);"abbc" → "bb"(偶数长度,中心在两个 b 之间)。
约束:字符串长度 1 ~ 1000;字符只含数字和英文字母。
思路
一句话:回文是“关于中心对称”的字符串,枚举中心向两边扩,谁扩得最远谁就是最长回文子串。
先想最直白的:枚举所有子串的左右端点 (i, j),再逐个字符验证是否回文。子串有 O(n²) 个,每个验证 O(n),总时间 O(n³)。n 到 1000 时约 10⁹ 次操作,会超时。
关键观察:一个子串是不是回文,完全由它的中心决定。中心分两类:
- 奇数长度:中心落在某个字符上,如
"racecar"的中心是e。 - 偶数长度:中心落在两个字符之间,如
"bb"的中心在两个b中间。
所以只需要枚举这 2n-1 个中心(n 个字符 + n-1 个空隙),从中心同时向左右扩:两侧字符相同就继续,不同就停。
为什么更省:每个中心的扩展是连续的一段匹配,一旦失配立刻停,内部不会重复验证;而且只需保存“当前最长”的起点与长度,不需要存下所有子串状态。与区间 DP(dp[i][j] 表示 s[i..j] 是否回文)相比,两者都是 O(n²) 时间,但 DP 要 O(n²) 空间存整张表,中心扩展只需 O(1) 空间,所以选它。
解法
维护 bestStart(当前最长回文的起点)和 bestLen(长度),从 0 / 1 起步,逐个中心扩展:
- 枚举中心
i:奇数中心(i, i),偶数中心(i, i+1)。 - 扩展:只要
left >= 0、right < n且s[left] == s[right],就left--、right++。 - 循环停止时,真正的回文区间是
(left+1, right-1),长度right - left - 1;比历史最长更长就更新。 - 最后返回
s.substr(bestStart, bestLen)。
以 s = "abbc" 走一遍:
| 中心 i | 类型 | 初始窗口 | 扩展到的回文(长度) |
|---|---|---|---|
| 0 | 奇数 (0,0) | a | “a” (1) |
| 0 | 偶数 (0,1) | a|b 不等 | 无 (0) |
| 1 | 奇数 (1,1) | b | “b” (1) |
| 1 | 偶数 (1,2) | b|b 相等 | “bb” (2) ← 最终答案 |
| 2 | 奇数 (2,2) | b | “b” (1) |
| 2 | 偶数 (2,3) | b|c 不等 | 无 (0) |
| 3 | 奇数 (3,3) | c | “c” (1) |
每个中心最多扩 O(n),所以总共 O(n²)。
下图把上面 4 个步骤串成流程,并逐步演示:


C++
class Solution {
public:
string longestPalindrome(string s) {
int n = (int)s.size();
int bestStart = 0, bestLen = 1; // 至少一个字符,必是回文
for (int i = 0; i < n; ++i) {
expand(s, i, i, bestStart, bestLen); // 奇数长度:中心是 s[i]
expand(s, i, i + 1, bestStart, bestLen); // 偶数长度:中心在 s[i] 与 s[i+1] 之间
}
return s.substr(bestStart, bestLen);
} private:
void expand(const string& s, int left, int本篇为付费内容
您已阅读试读部分,购买后即可解锁全文。
⚠️ 本站内容已做唯一性标记并受版权保护,未经授权禁止转载,侵权必究。
还没有评论,来说两句?