LeetCode 5. 最长回文子串——中心扩展法彻底讲透

LeetCode 5. 最长回文子串------中心扩展法彻底讲透

一、题目描述

给定一个字符串 s,找到其中最长的回文子串,并返回这个子串。

示例:

text 复制代码
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
text 复制代码
输入:s = "cbbd"
输出:"bb"

二、什么是回文串?

回文串(Palindrome):

正着读和反着读完全一样的字符串。

例如:

text 复制代码
"a"
"aa"
"aba"
"abba"
"racecar"

都属于回文串。

而:

text 复制代码
"ab"
"abc"
"abca"

不是回文串。


三、回文的核心性质:左右对称

所有回文串一定存在一个「对称中心」。

例如:

奇数长度回文

text 复制代码
aba

a ← b → a

中心是:

text 复制代码
b

偶数长度回文

text 复制代码
abba

ab ← → ba

中心是:

text 复制代码
两个字符之间的缝隙

因此:

只要找到所有可能的对称中心,然后不断向两边扩散,就能找到所有回文串。

这就是中心扩展法。


四、整体思路

对于字符串中的每一个位置 i

我们都尝试两种情况:

情况1:奇数回文

python 复制代码
expand(i, i)

例如:

text 复制代码
aba
 ^

情况2:偶数回文

python 复制代码
expand(i, i + 1)

例如:

text 复制代码
abba
  ^^

每次扩散结束后:

记录当前回文的长度。

如果比之前最长的回文还长:

就更新答案。


五、扩散函数怎么写?

定义函数:

python 复制代码
expand(left, right)

表示:

leftright 开始向两边扩散。


扩散条件

三个条件必须同时满足:

python 复制代码
left >= 0
right < len(s)
s[left] == s[right]

满足条件:

python 复制代码
left -= 1
right += 1

继续扩散。


为什么最后要回退?

例如:

text 复制代码
aba

扩散过程:

text 复制代码
left=1 right=1

↓

left=0 right=2

↓

left=-1 right=3

退出循环时:

text 复制代码
left=-1
right=3

已经越界了。

真正有效的边界应该是:

text 复制代码
0 ~ 2

因此返回:

python 复制代码
left + 1
right - 1

六、完整代码

python 复制代码
class Solution:
    def longestPalindrome(self, s: str) -> str:
        # 长度小于2直接返回
        if len(s) < 2:
            return s

        start = 0
        end = 0

        def expand(left, right):
            while (
                left >= 0
                and right < len(s)
                and s[left] == s[right]
            ):
                left -= 1
                right += 1

            return left + 1, right - 1

        for i in range(len(s)):

            # 奇数长度回文
            l1, r1 = expand(i, i)

            # 偶数长度回文
            l2, r2 = expand(i, i + 1)

            # 更新最长奇数回文
            if r1 - l1 > end - start:
                start = l1
                end = r1

            # 更新最长偶数回文
            if r2 - l2 > end - start:
                start = l2
                end = r2

        return s[start:end + 1]

七、拿示例跑一遍

字符串:

text 复制代码
s = "babad"

i = 0

奇数:

text 复制代码
b

长度:

text 复制代码
1

偶数:

text 复制代码

最长:

text 复制代码
b

i = 1

奇数:

text 复制代码
bab

长度:

text 复制代码
3

更新答案:

text 复制代码
bab

偶数:

text 复制代码

i = 2

奇数:

text 复制代码
aba

长度:

text 复制代码
3

和当前最长相同。

保持:

text 复制代码
bab

最终返回:

text 复制代码
bab

八、为什么 i + 1 不会越界?

假设:

text 复制代码
s = "abc"

最后一次:

python 复制代码
i = 2
expand(2, 3)

进入函数:

python 复制代码
right < len(s)

即:

python 复制代码
3 < 3

不成立。

直接退出。

不会报错。


九、复杂度分析

时间复杂度

text 复制代码
O(n²)

共有:

text 复制代码
2n 个中心

每个中心最多扩散:

text 复制代码
O(n)

因此:

text 复制代码
O(n²)

空间复杂度

text 复制代码
O(1)

只使用有限几个变量。


十、高频易错点

1、只处理奇数回文

错误:

python 复制代码
expand(i, i)

遗漏:

python 复制代码
expand(i, i + 1)

会导致:

text 复制代码
"cbbd"

输出错误。


2、扩散使用 if

错误:

python 复制代码
if s[left] == s[right]:

只能扩一层。

必须使用:

python 复制代码
while

持续扩散。


3、忘记回退边界

错误:

python 复制代码
return left, right

应该:

python 复制代码
return left + 1, right - 1

4、长度公式少写 +1

错误:

python 复制代码
right - left

正确:

python 复制代码
right - left + 1

5、字符串切片忘记 +1

错误:

python 复制代码
s[start:end]

正确:

python 复制代码
s[start:end + 1]

因为 Python 切片:

text 复制代码
左闭右开

十一、一句话总结

最长回文子串的核心思想:

枚举每一个可能的回文中心,从中心向两边不断扩散,记录最长回文的左右边界。

记忆口诀:

text 复制代码
一个字符找奇数,
两个字符找偶数;
左右不断往外扩,
记录最长回文串。