LeetCode-005:从中心扩散找最长回文子串,可行吗?
摘要:本题在线练习:LeetCode 5. 最长回文子串 - 在线练习(免费 · 无需登录 · AI 辅助)(https:onefly.topzero2Leetcodeplaygroun
本题在线练习:LeetCode 5. 最长回文子串 - 在线练习(免费 · 无需登录 · AI 辅助)(https://onefly.top/zero2Leetcode/playground.html?id=5)
配套刷题网站 Zero2Leetcode —— 内置本地 OJ + AI 教练,零门槛开始刷 Hot 100。
题目概述
给定一个字符串 s,返回它的最长回文子串。
回文的定义:从左读和从右读一样,比如 aba、abba。
这题最容易走偏的点是:很多人一上来就想“枚举所有子串”,那是 O(n^3),会超时。更自然的思路是:回文一定有一个“中心”,从中心向两边扩展即可。
核心思路:回文的“中心”只有两种
回文子串的中心位置:
奇数长度回文:中心是一个字符,例如 aba 的中心是 b
偶数长度回文:中心是两个字符之间的缝隙,例如 abba 的中心在两个 b 之间
因此只要枚举每个可能中心(2n-1 个),对每个中心做“左右扩散”,就能在 O(n^2) 内求解。
先从最自然的思路讲起:中心扩散
代码实现(中心扩散)
class Solution:
def longestPalindrome(self, s: str) -> str:
n = len(s)
if n <= 1:
return s
def expand(l: int, r: int) -> tuple[int, int]:
# 返回最长回文的左右边界(闭区间)
while l >= 0 and r < n and s[l] == s[r]:
l -= 1
r += 1
return l + 1, r - 1
best_l, best_r = 0, 0
for i in range(n):
l1, r1 = expand(i, i) # 奇数回文
l2, r2 = expand(i, i + 1) # 偶数回文
if r1 - l1 > best_r - best_l:
best_l, best_r = l1, r1
if r2 - l2 > best_r - best_l:
best_l, best_r = l2, r2
return s[best_l:best_r + 1]
逐行拆解:expand 为什么“退一步”返回
expand(l, r) 的循环条件是“还能扩散且两侧字符相等”。当循环退出时,说明 (l, r) 这一对已经不合法了。
所以真正的回文边界要“退回一步”,即返回 l+1, r-1。
手动模拟:s = "babad"
取中心 i=1(字符 a):
初始 (l,r)=(1,1),匹配 a
扩散到 (0,2),匹配 b 和 b
再扩散到 (-1,3) 越界,停止
返回 (0,2),回文是 "bab"。
取中心 i=2(字符 b)则会得到 "aba"。两者长度一样,返回任意一个都符合题意。
进阶解法:动态规划(可选)
动态规划也能做,但比中心扩散更绕,通常不建议作为第一选择。
定义 dp[i][j] 表示 s[i..j] 是否是回文:
如果 s[i] != s[j],则不是回文
如果 s[i] == s[j],则需要 dp[i+1][j-1] 为真(或者长度小于等于 2 时天然成立)
DP 的时间也是 O(n^2),空间 O(n^2)。
复杂度分析
中心扩散:
时间:最坏 O(n^2)(每个中心最多扩散 O(n))
空间:O(1)
总结
这题最重要的不是“记住代码”,而是形成一个直觉:
最长回文不是从“枚举子串”开始,而是从“枚举中心、向两边扩散”开始。
把奇数中心与偶数中心都处理到位,这题就稳定了。
