今天在刷了几道简单的动态规划后,又看了看string方面的题
第五题 Longest Palindromic Substring
题目的意思:求一个字符串的最长回文子串
分析:开始,我的想法是,现在字符串中插入特殊字符,比如'$'
,这样可以规避回
文串长度为奇数或偶数的讨论问题,加入特殊字符后,长度始终为奇数。然后建立一个数组,该数组记录了以每一位为中心的回文字符串的长度。解法很简单:
def findmin(s):
s_ = '~' + '~'.join(s) + '~'
st = ''
p = [0 for i in range(len(s_))]
for i in range(len(s_)):
if i == 0 or i == len(s_)-1:
p[i] = 1
else:
temp = 0
for j in range(min(i, len(s_)-i-1)+1):
if s_[i-j] != s_[i+j]:
break
temp += 1
p[i] = temp
position = p.index(max(p))
for i in range(max(p)):
if s_[position+i] != '~':
st += s_[position+i]
if (max(p)-1) % 2 == 0:
st = st[::-1] + st
else:
st = st[:0:-1] + st
return st
可是提交上去后,结果是运行超时...(时间复杂度比较高,没通过) 然后我百度了一下这个问题,找到了一个比较nb的算法,复杂度为O(n)
Manacher
算法: 一开始和我的想法类似,填充特殊字符,保证回文串长度为奇数。Manacher算法用一个辅助数组p[i]表示以字符T[i]为中心的最长回文字串的最右字符到T[i]的长度,比如以T[i]为中心的最长回文字串是T[l,r],那么p[i]=r-i+1。p[i]-1就是该回文子串在原字符串S中的长度。
Len数组的计算 首先从左往右依次计算p[i],当计算p[i]时,pj已经计算完毕。设P为之前计算中最长回文子串的右端点的最大值,并且设取得这个最大值的位置为id
总结一下,Manacher算法通过记录已匹配的最右位置和对应的对称中心来跳过一些没用的比较
class Solution:
@param {string} s
@return {string}
def longestPalindrome(self, s):
s ='$'+'$'.join(s)+'$'
mx = 0 #之前计算的最长的回文子串长度所能到达的最后边界
id_ = 0 #mx对应的中心
p = [0 for i in range(len(s))]
st = ''
for i in range(len(s)):
#i在边界内
if mx > i:
p[i] = min(p[2*id_-i], mx-i)
#i在边界外,需要从头开始匹配
else:
p[i] = 1
while i-p[i] >= 0 and i+p[i] < len(s) and s[i-p[i]] == s[i+p[i]]:
p[i] += 1
#如果新计算的回文串右端点位置大于mx,需要更新mx与id_
if mx < p[i] +i:
mx = p[i] +i
id_ = i
#输出字符串
position = p.index(max(p))
for i in range(max(p)):
if s[position+i] != '$':
st += s[position+i]
if (max(p)-1) % 2 == 0:
st = st[::-1] + st
else:
st =st[:0:-1] + st
return st