题目描述
思路解析动画文字版
最直接的笨办法:枚举起点和终点(n² 个子串),每个子串再花 O(n) 判断是否回文,合起来 O(n³)。换个思路:回文一定有个中心,从中心往两边扩,能省一个量级。
为什么成立:回文关于中心镜像对称,知道中心就能向外验证。对每个中心,l、r 一起向外走,只要 s[l]==s[r] 就继续扩;一旦不等或越界就停。中心有两种——单字符(奇回文)和两字符之间(偶回文),都试一遍取最长。
奇中心 · 以下标 1 的 a 出发:从中心 a(下标 1)开始,l=r=1。单字符本身就是回文,作为起点向两边扩。
向外扩 · s[0]==s[2] ✓:l 退到 0(b)、r 进到 2(b),s[0]==s[2],扩成功!当前回文 "bab"、长度 3,刷新最长记录。继续往外扩。
再扩 · l 越界,停:转折:l 再退一步就到 −1 越界,扩展终止。以 a 为奇中心扩出的最长回文是 "bab"(长度 3)。换下一个中心。
偶中心 · 下标 0、1 之间(负例):试偶中心(下标 0、1 之间):l=0 是 b、r=1 是 a,s[0]≠s[1],第一步就不相等,一次都没扩成,这个偶中心贡献长度 0。这就是负例:不等立刻停。但偶中心也能扩成功——换个串看正例。
偶中心正例 · 迷你串 "abba" 的 b、b 之间:换个迷你串 "abba" 看偶中心扩成功:偶中心从两字符之间起步,l=1、r=2,s[1]==s[2](b==b),第一步就成回文 "bb"(长度 2)。这正是奇中心做不到的——单字符中心起步长度只能是奇数。继续往外扩。
偶中心正例 · 再扩 s[0]==s[3] ✓ 扩出 "abba":l 退到 0(a)、r 进到 3(a),s[0]==s[3],扩成功!偶回文 "abba"(长度 4)整个扩出来了。再走 l 就到 −1 越界、停。这就是亲眼看见 (i,i+1) 偶中心也能扩出东西——和奇中心 bab/aba 形成对照。
奇中心 · 以下标 2 的 b 出发:再换中心:以下标 2 的 b 为奇中心,l=r=2,单字符回文起步,向两边扩。
向外扩 · s[1]==s[3] ✓:l 退到 1(a)、r 进到 3(a),s[1]==s[3],扩成功!回文 "aba"、长度 3,和当前最长打平。继续扩。
再扩 · s[0]≠s[4],停(负例):继续扩到 l=0(b)、r=4(d),s[0]≠s[4],不相等,扩展停止。以 b 为中心扩出的最长仍是 "aba"(长度 3)。
收一格的关键 · while 停在不等处:最容易错的一格:while 跳出时,l、r 已经各多走一步、踩在了不等的 b 和 d 上。所以真正的回文不是 [l, r],而是收回一格的 [l+1, r-1],也就是下标 1 到 3 的 "aba"。记住这个 +1/−1。
所有中心扫完 · 最长长度 3:把每个奇中心、偶中心都扩一遍:最长是 "bab" 和 "aba",长度都为 3。返回其一即可。
抓住"回文关于中心对称"这一点,就把"枚举子串 + 判回文"两件事并成一件:从中心往外扩,扩到哪算到哪。回文子串计数(647)、最长回文子序列(516)都和它同源。
迁移阶梯:把这题练到能复述后,再去同类题里迁移:先复用 expand 骨架,再调整边界条件。卡壳时点右下角问问小欧 AI 私教,让它陪你把 647 的"累加"改法推一遍。
边界三连:边界三连:极端输入先过一遍。
几个高频追问,记住答法。
参考代码
class Solution: def longestPalindrome(self, s): ans_l = ans_r = 0 def expand(l, r): while l >= 0 and r < len(s) and s[l] == s[r]: l -= 1 r += 1 return l + 1, r - 1 for i in range(len(s)): for l, r in (expand(i, i), expand(i, i + 1)): if r - l > ans_r - ans_l: ans_l, ans_r = l, r return s[ans_l:ans_r + 1]复杂度
- 时间复杂度:O(n²),n 个中心 × 每个中心最多扩 n/2 步
- 空间复杂度:O(1),只用 l、r 几个指针
可套用的代码模板
记住骨架:一个 expand 函数返回"该中心的回文长度"、每个位置试奇偶两种中心、相等就向外扩。和上面参考代码的区别别懵:参考版 expand 返回的是区间端点 [l+1, r-1],方便直接切串取子串;这里模板版直接返回长度 r-l-1,迁移到 647 计数题更顺手——两者是同一个 expand 的两种收尾。三语言长度公式都是 r-l-1(已收回一格)。把最后一行 max 换成"扩成功就累加",就是回文子串计数(647)。
# 骨架:一个 expand + 每位置试奇偶两种中心def expand(l, r): # 传入初始左右指针 while l >= 0 and r < n and s[l] == s[r]: # 两边相等就扩 l -= 1; r += 1 return r - l - 1 # 收一格后的回文长度for i in range(n): odd = expand(i, i) # 奇中心: 单字符 even = expand(i, i + 1) # 偶中心: 两字符之间 best = max(best, odd, even) # 按需求改这里易错点
面试追问把动画讲成自己的话
追问中心扩展和 DP 怎么选?
追问有 O(n) 解法吗?
追问暴力为什么 O(n³)?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
回文子串
LeetCode 647 · 中等 · 沿着 一维动态规划 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题