对于最长回文子串,使用其镜像性可以达到一时间复杂度为o(n)的算法。
1.首先对于字符串,无论长度为奇数或偶数,经过相邻两位之间添加未在串中出现的如“#”类字符(为了避免越界首尾也要添加),然后再在首位和尾部添加不同于#和串内字符的两种字符^和$,使其长度均变为奇数。
2.之后对于新字符串S,假设目前所知道的最长的字符串中心为center,边界分别为LEFT和RIGHT,目前搜索到的为第i位。
新建一个长度等于S的数组P=[0]*len(s) 用于表示i位的回文最长半径,之后可进行如下判断:
1.RIGHT>=i:
由于回文串的镜像性,i在center左边,LEFT右边有一个对应下标j=2center-i,且对于i和j到边界LEFT,RIGHT的长度相同,之后可进行第二次分类:
A.i+p[i]<=RIGHT
B.i+p[i]>RIGHT
均满足p[i]=min(p[j],RIGHT-i)
2.RIGHT<i:
P[i]=0
之后对于情况B以及2的情况,只需要继续进行RIGHT外的比较,可采用while循环,满足条件时 半径加一。
之后将中心定为i,右边界定为p[i]+I。
3.最后只需要采用循环寻找出最大的半径以及其中心,然后计算出起始点,由于之前阔倍的原因,起始点为MAX_I-1-p[MAX_I],由于p[i]为扩充后的半径,正好是子串的长度。
class Solution:
def longestPalindrome(self, s: str) -> str:
l=len(s)
t='^'
center,right=0,0
if s=='' or len(s)==1:
return s
for c in s:
t=t+'#'+c
t=t+'#'+'$'
p=[0]*len(t)
for i in range(1,len(t)-1):
j=2*center-i
if right>i:
p[i]=min(right-i,p[j])
else:
p[i]=0
while(t[i-1-p[i]]==t[i+1+p[i]]):
p[i]+=1
if right<i+p[i]:
center=i
right=i+p[i]
max_i=0
for i in range(1,len(t)-1):
if p[i]>p[max_i]:
max_i=i
start = round((max_i-1-p[max_i])/2)
return s[start:start+p[max_i]]