最长回文子串
给你一个字符串 s,找到 s 中最长的回文子串。
如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。
示例 1:
输入:s = "babad"
输出:"bab"
解释:"aba" 同样是符合题意的答案。
示例 2:
输入:s = "cbbd"
输出:"bb"
最优解决方案
有很多算法可以解决这个问题,但是其中最快的算法是Manacher(马拉车)算法,它的时间复杂度是 O(n),也就是说它只需要扫描一遍字符串就可以找到最长的回文子串。
具体来说,Manacher算法需要以下几个步骤:
- 预处理字符串:在每个字符之间插入一个特殊字符(如 #),并在首尾加上不同的特殊字符(如 ^ 和 $),这样做的目的是消除奇偶性差异,使得所有回文子串都有奇数长度。
- 初始化辅助数组 p:p[i] 表示以 i 为中心的最长回文半径,即 p[i]-1 就是以 i 为中心的最长回文子串长度。
- 遍历预处理后的字符串:维护两个变量 center 和 right,分别表示当前已知回文子串能够达到右侧最远位置 right 的那个回文子串对应的中心位置 center 和右侧边界 right23。对于每个位置 i ,有以下三种情况:
- 如果 i > right ,说明 i 不在当前已知回文子串能够覆盖到的范围内,那么就需要从头开始比较以 i 为中心向两边扩展是否相等,并更新 p[i], center 和 right 的值。
- 如果 i <= right ,说明 i 在当前已知回文子串能够覆盖到的范围内,那么就可以利用对称性和已知信息来优化比较过程。具体地说,令 j = 2 * center - i 表示关于 center 对称于 i 的位置,则有以下两种情况:
- 如果 p[j] < right - i ,说明以 j 为中心和以 i 为中心对应部分完全相同,并且不会超出右侧边界 right ,那么就可以直接令 p[i] = p[j]。
- 如果 p[j] >= right - i ,说明以 j 为中心和以 i 为中心对应部分可能超出右侧边界 right 或者恰好达到右侧边界 right ,那么就需要从 right + 1 开始比较以 i 为中心向两边扩展是否相等,并更新 p[i], center 和 right 的值。
- 找出最大值:遍历完预处理后的字符串后,在数组 p 中找出最大值 maxLen 和其对应下标 maxCenter ,则原始字符串 s 中最长回文子串长度为 maxLen-1 ,起始位置为 (maxCenter-maxLen)/2 (注意还原预处理后下标和长度)。
假设原始字符串 s = “babad” ,则预处理后的字符串为 “^#b#a#b#a#d#$” ,辅助数组 p 的初始值为 [0, 0, 0, 0, 0, 0, 0, 0, 0] 。下面是遍历过程中的情况:
- 当 i = 1 时,i > right ,所以从头开始比较以 i 为中心向两边扩展是否相等,发现没有相等的字符,所以 p[1] = 1 ,center 和 right 都不变。
- 当 i = 2 时,i > right ,所以从头开始比较以 i 为中心向两边扩展是否相等,发现有一个相等的字符 # ,所以 p[2] = 2 ,center = i , right = i + p[i] -1 =3。
- 当 i = 3 时,i <= right ,令 j = center *2 -i=1,则 p[j] < right -i=0 <1,所以直接令 p[3] = p[j]=1。
- 当 i =4时,i <=right ,令 j=center *2-i=2,则p[j]=right-i=2>=2,所以从right+1=4开始比较以i为中心向两边扩展是否相等,发现有两个相等的字符a和#,所以p[4]=4+2=6,center=i,right=i+p[i]-1=9。
- 当 i=5时,i<=right ,令j=center*2-i=3,则p[j]<right-i=4<5,所以直接令p[5]=p[j]=1。
- 当 i=6时,i<=right ,令j=center*2-i=4,则p[j]>=right-i=6>=3,所以从right+1=10开始比较以i为中心向两边扩展是否相等,发现没有相等的字符(超出字符串范围),所以p[6]=right-i+1=4。
- 当 i=7时,i<=right ,令j=center*2-i=5,则p[j]<right-i=2<4,所以直接令p[7]=p[j]=1。
- 当 i=8时,i>right ,所以从头开始比较以i为中心向两边扩展是否相等,发现没有相等的字符(超出字符串范围),所以p[8]=1。
- 遍历完预处理后的字符串后,在数组 p 中找出最大值 maxLen 和其对应下标 maxCenter ,则原始字符串 s 中最长回文子串长度为 maxLen-1 ,起始位置为 (maxCenter-maxLen)/2 (注意还原预处理后下标和长度)。在这个例子中, maxLen=p[4]=6,maxCenter=i,len=maxLen-1=(maxCenter-maxLen)/2=(4-6)/2=-1。因此原始字符串s中最长回文子串是"bab"。