- 解法一
枚举每个回文的中心(回文中心可能是一个字符,也可能是两个字符,例子分别是:cac
,caac
),从这个回文中心出发向两边扩散,直到扩到不能再扩,记录下该回文中心能够产生的最长回文字符串的起始、终止位置;
与全局最长回文字符串的起始、终止位置比较,记录下最长回文字符串。
python
class Solution:
def expand(self,s,l,r):
while l>=0 and r<len(s) and s[r]==s[l]:
l-=1
r+=1
return l+1,r-1
def longestPalindrome(self, s: str) -> str:
start,end=0,0
for i in range(len(s)):
left1,right1=self.expand(s,i,i)
left2,right2=self.expand(s,i,i+1)
if right1-left1>end-start:
start,end=left1,right1
if right2-left2>end-start:
start,end=left2,right2
return s[start:end+1]
解法2: Manacher算法
在上面枚举每一个回文中心 向左右两边扩散的解法的基础上,考虑如何复用已经求出来的信息。
现在仅考虑长度为奇数的字符串,
假设以索引j为中心的最长回文字符串(回文字符串的一半的长度为arm_len[j])已经找到,问如何利用这一信息求之后的索引i作为回文中心时的长度?
对于之后的索引i,
- 首先,求以
s[i]
为中心时最长回文子串的臂长,记为cur_arm_len
;
这个过程分为两个步骤,
首先,利用之前索引j
的回文臂长信息arm_len[j]
和目前最长回文子串能够到达的最远位置right
,找到以s[i]
为中心且不超过当前已知最长回文子串范围时的回文子串的臂长min_arm_len
;(注:如果目前已知的以s[j]
为中心的回文子串并不能覆盖i
,那么arm_len[j]
信息没用,所以可以跳过求min_arm_len
这一步骤)
接着,在min_arm_len
的基础上,利用方法self.expand
,继续向左右探索以s[i]
为中心时可能的回文片段,直到不对称为止,并返回回文臂长。 - 接着,记录以
s[i]
为中心时最长回文子串的臂长到数组arm_len
中; - 然后,更新最近一次的已知回文中心
j
和最近一次的回文子串的最大索引right
; - 最后,更新全局变量
start,end
(最长回文子串的起始、末尾位置)。
python
class Solution:
def expand(self,s,l,r):
while l>=0 and r<len(s) and s[l]==s[r]:
l-=1
r+=1
return (r-l-2)//2
def longestPalindrome(self, s: str) -> str:
start,end=0,0
s='#'+'#'.join(s)+'#'
arm_len=[]
right=-1
j=-1
for i in range(len(s)):
if i<=right:
i_sym=2*j-i
min_arm_len=min(right-i,arm_len[i_sym])
cur_arm_len=self.expand(s,i-min_arm_len,i+min_arm_len)
else:
cur_arm_len=self.expand(s,i,i)
arm_len.append(cur_arm_len)
if i+cur_arm_len>right:
right=i+cur_arm_len
j=i
if 2*cur_arm_len+1>end-start:
start,end=i-cur_arm_len,i+cur_arm_len
return s[start+1:end+1:2]