动态规划解法:时间复杂度 o(n2),空间复杂度O(n2)
dp[i][j]表示当s[i:j]子串是否是回文串。
当j-i<=1时,如果s[i] == s[j]则表示s[i:j]为回文串,dp[i][j]=1
当j-i > 1时,则判断 s[i]、s[j]是否相等以及dp[i+1][ j-1]是否为true,即s[i+1:j-1]是否为回文串,如果为真,则dp[i][j] = true
#最长回文子串
def longestPalindrome(s):
dp=[[0 for i in range(len(s))] for j in range(len(s))]
logestSubStr = "" # 存储最长回文子串
logestLen = 0 # 最长回文子串的长度
for j in range(len(s)):
for i in range(j+1):
if j-i<=1:
if s[i]==s[j]:
dp[i][j]=1
if logestLen<j-i+1:
logestLen=j-i+1
logestSubStr=s[i:j+1]
elif j-i>1:
if s[i]==s[j] and dp[i+1][j-1]==1:
dp[i][j]=1
if logestLen<j-i+1:
logestLen=j-i+1
logestSubStr=s[i:j+1]
return logestLen
while True:
try:
arr=input()
lent=longestPalindrome(arr)
print(lent)
except:
break
法二:另一种是(时间复杂度为O(n):
理论支持:每当增加一个新的字母,最大回文串的长度只能增加1或者2,不可能增加更多,并且,新的最大回文串必然要包含这个字母
证明:如果新增了一个字母,最大回文串的长度增加了3,这是不可能的,例如:abcdefgfedcba,当增加到最后的b或者a时,是不可能增加3个长度的,因为每增加一个字母,前面必然已经存在一个回文子串,且长度比新回文串串小1或者小2.
所以,从头到尾扫描字符串,每增加一个新的字符,判断以这个字符结尾,且长度为maxLen+1或者maxLen+2的子串是否为回文,如果是,更新最大回文子串
def longestPalindrome(s):
if s==s[::-1]:
return len(s)
maxLen=0
for i in range(len(s)):
if i-maxLen>=1 and s[i-maxLen-1:i+1]==s[i-maxLen-1:i+1][::-1]:
maxLen+=2
continue
if i-maxLen>=0 and s[i-maxLen:i+1]==s[i-maxLen:i+1][::-1]:
maxLen+=1
return maxLen
while True:
try:
a=input()
if a:
print(longestPalindrome(a))
except:
break
法三:根据回文串对称性,分为奇数长度回文串和偶数长度回文串
def match(s):
res=0
for i in range(len(s)-1):
if s[i]==s[i+1]:#奇数长度
first=i
end=i+1
while first>=0 and end<len(s) and s[first]==s[end]:
#满足条件就向两边扩展
first-=1
end+=1
res=max(res,end-first-1)
elif s[i-1]==s[i+1]:#偶数长度
first=i-1
end=i+1
while first>=0 and end<len(s) and s[first]==s[end]:
first-=1
end+=1
res=max(res,end-first-1)
return res
match('abba')
法四:Manacher算法
首先对字符串做一个预处理,在所有的空隙位置(包括首尾)插入同样的符号,要求这个符号是不会在原串中出现的。这样会使得所有的串都是奇数长度的。插入同样的符号且首尾都有,不影响回文性。
Manacher算法用一个辅助数组Len[i]表示以字符T[i]为中心的最长回文字串的最右字符到T[i]的长度,即以T[i]为中心的最长回文串半径,比如以T[i]为中心的最长回文字串是T[l,r],那么Len[i]=r-i+1。
Len数组有一个性质,那就是Len[i]-1就是该回文子串在原字符串S中的长度,至于证明,首先在转换得到的字符串T中,所有的回文字串的长度都为奇数,那么对于以T[i]为中心的最长回文字串,其长度就为2*Len[i]-1,经过观察可知,T中所有的回文子串,其中分隔符的数量一定比其他字符的数量多1,也就是有Len[i]个分隔符,剩下Len[i]-1个字符来自原字符串,所以该回文串在原字符串中的长度就为Len[i]-1。 有了这个性质,那么原问题就转化为求所有的Len[i]。
设mxright为之前计算中最长回文子串的右端点的最大值,并且设取得这个最大值的回文串的对称点位置为mxid,分两种情况:
一:i<=mxright:
找出i关于mxid的对称点j,j=2*mxid-i
如果len[j]<mxright-i,则以j为中心的回文串一定全部在以mxid为中心的回文串中,则以I为中心的回文串半径最低为len[j],即len[i]>=len[j],
如果len[j]>=mxright-i,则以i为中心的回文串可能会延伸到mxright之外,而大于mxright的部分我们还没有进行匹配,所以要从mxright+1位置开始一个一个进行匹配,直到发生失配,从而更新mxright和对应的mxid以及Len[i]。
二:i>mxright
因为mxright右边字符串还没开始匹配,所以以i为中心从左右两边开始匹配,然后更新mxid和mxright位置
def match(s):
s='#'+'#'.join(s)+'#'
p=[0]*len(s)
mx=0
mid=0
mxlen=0
for i in range(len(s)):
#i在已经匹配过的最长回文子串的右边,以i为中心的回文串最短半径为min(p[j],mx-i),因为p[j]长度可能超过当前回文子串右边界
if i<mx:
p[i]=min(p[2*mid-i],mx-i)
else:
p[i]=1
#边界条件
while i+p[i]<len(s) and i-p[i]>=0 and s[i-p[i]]==s[i+p[i]]:
p[i]+=1
if p[i]+i-1>mx:
mid=i
mx=p[i]-i+1
mxlen=max(p[i],mxlen)
return mxlen-1