LeetCode 647. 回文子串
题目描述

题意分析
统计字符串中有多少个连续区间正着读、反着读都相同,即回文子串的数量。每个非空区间按起止下标区分,即使内容相同,只要位置不同也分别计数。
单个字符也是回文,奇数长度和偶数长度都要统计。只返回数量,不需要列出字符串,也不能按内容用集合去重;子串必须连续,不能跳过中间字符。
解法:中心扩展
核心思路
[!blue]
回文的两侧关于中心对称。若一个内部区间已经是回文,只要它外侧新增的两个字符相等,更大的区间仍是回文,因此可以从中心开始逐层验证,而不必为每个区间重新检查所有字符。
奇数长度的中心是某个字符,从
(i, i)开始;偶数长度的中心在相邻字符之间,从(i, i + 1)开始。对每个下标分别启动这两种扩展,就覆盖了全部可能的中心。扩展过程中,只要左右下标有效且字符相等,当前区间就是一个回文,计数加一,再同时向外移动。第一次比较时,奇数中心内部只有一个字符,偶数中心内部为空,二者都满足回文基础条件。
若一对字符不相等,同中心的所有更大区间都会包含这对不匹配字符,所以可以立即停止。计数应在每次扩展成功时增加,同一中心可以对应多个不同长度的回文,而不是只记录最长的一个。
任意回文区间都有唯一的中心与长度,必然在对应中心的某一轮扩展中被计算,其他中心不会生成同一个区间,因此不重不漏。末尾的偶数中心右侧越界,直接贡献零,不影响结果。
解题步骤
- 遍历每个下标
i,分别调用扩展函数处理(i, i)和(i, i + 1)。- 扩展函数先检查两端下标,再比较字符;有效且相等时计数加一。
- 执行
left--、right++,继续检查更大区间,直到越界或失配。- 累加每个中心的扩展次数,返回全部回文区间的数量。
代码实现
class Solution {
public int countSubstrings(String s) {
int ans = 0;
for (int center = 0; center < s.length(); center++) {
ans += expand(s, center, center);
// 字符之间作为偶数回文中心,和单字符中心互补且不会重复。
ans += expand(s, center, center + 1);
}
return ans;
}
private int expand(String s, int left, int right) {
int count = 0;
while (left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) {
// 每成功扩展一层,恰好多出一个以当前中心确定的回文子串。
count++;
left--;
right++;
}
return count;
}
}
func countSubstrings(s string) int {
ans := 0
for center := 0; center < len(s); center++ {
ans += expandPalindrome(s, center, center)
// 字符之间作为偶数回文中心,和单字符中心互补且不会重复。
ans += expandPalindrome(s, center, center+1)
}
return ans
}
func expandPalindrome(s string, left int, right int) int {
count := 0
for left >= 0 && right < len(s) && s[left] == s[right] {
// 每成功扩展一层,恰好多出一个以当前中心确定的回文子串。
count++
left--
right++
}
return count
}
复杂度分析
- 时间复杂度:$O(n^2)$。共有 $2n-1$ 个中心,每个中心最坏向外扩展 $O(n)$ 层;全相同字符串会达到该上界。
- 空间复杂度:$O(1)$,只使用计数器和左右指针。
关键点总结
[!green]
- 回文既可能以字符为中心,也可能以两个字符之间的空隙为中心。
- “中心 + 扩展半径”唯一确定一个回文子串,这是不重不漏的依据。
- 本题按下标区间计数,不按字符串内容去重。
- 与“最长回文子串”共用同一扩展模板:本题每次成功就计数,后者更新最长区间。
易错点总结
[!yellow]
- 只枚举字符中心会漏掉偶数长度回文,必须同时处理字符之间的中心。
- 按内容去重会合并不同位置的区间,与题目按出现位置计数的要求不符。
- 内层已经失配时不能继续外扩,外层相等无法修复内部的不对称。
- 必须先判断边界再访问字符,扩展后的下标可能为负或达到字符串长度。
- 每次成功扩展都形成一个新区间,不能每个中心只计一次,也不能只计最长回文。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 5. 最长回文子串 | 中等 | 同样可中心扩展,原题保留最长区间,本题把每次成功扩展计为一个回文子串。 |
| 516. 最长回文子序列 | 中等 | 原题是可跳过字符的子序列,本题要求连续区间,状态与扩展方式不同。 |
| 131. 分割回文串 | 中等 | 用区间或中心扩展刻画回文结构;本题累计所有回文子串数量,该题预处理回文后枚举切分方案。 |
| 132. 分割回文串 II | 困难 | 用区间或中心扩展刻画回文结构;本题累计所有回文子串数量,该题在回文区间上递推最少切割次数。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!