今天在刷题的时候, 遇到了这道最长回文子串问题
Input: "babad" Output: "bab" Note: "aba" is also a valid answer.(1)第一次暴力解决,结果直接超时
def longestPalindrome(self, s):
self.start = 0
self.end = len(s) - 1
self.length = 0
self.result(s, self.start, self.end)
return s[self.start: self.end + 1]
def isPalindromic(self, s):
return s == s[-1::-1]
def result(self, s, start, end):
if (end - start + 1) > self.length:
if self.isPalindromic(s[start:end + 1]):
if (end - start + 1) > self.length:
self.length = end - start + 1
self.start, self.end = start, end
return
if start < end:
self.result(s, start + 1, end)
self.result(s, start, end - 1)
看到超时的时候,来看看自己的代码,我感觉我的电脑没炸就好,如果给定的字符串非常的长,并且回文串很短 ,这样的话很容易造成栈溢出, 运行一个未通过的测试案例, 结果程序就没有动作了, 相对较短的字符串,这种暴力的解决方法是可以的.
(2)想到最长公共子串.
开始的暴力方法让自己超时, 因为回文串是对称的, 所以可以去考虑 正向的字符串和反向的字符串的最长公共子串, 想法很好,自己给的测试用例也过了, 但是还是不对 abcdasdfghdcba,这样会得到abcd /(ㄒoㄒ)/~~,根本就不是回文串啊.....
代码想法很简单, 用一个二维数组记录最大的长度,这样可以得到最长公共子串.
a b b d
d 0 0 0 1
b 0 1 1 0
b 0 1 2 0
a 1 0 0 0
这是得到的二维矩阵, 但是上面的用例,我们是无法得到正确的结果. pass
(3)查看资料找到了manacher算法
这个算法是以每一个字符为中心, 向两边发散,同时,用一个数组p来记录以每一个字符为中心的回文串的一半的长度.
先看一下该算法的核心.
确定以第i个字符为中心的回文串长度,建立在p[1:i-1]这个数组基础上
假设, mx为当前字符串中已经确定的回文串的最有端. po为以mx为右端的回文串的中心,
这时候,我们需要确定 第i个字符的位置是 > ma 还是 i <= mx
如果 i <= mx 就是当前要确定的字符包含在前面的某个回文串里面, 回文串是对称的, 所以找到i 相对于po的对称位置 j,
接下来, 我们要做的就是判断, ①p[j] >= mx - i or ②p[j] < mx - i
在第一种情况, 就是说以i为中心的回文串还有向 mx右边伸展的可能, 然而那一部分还是未知数, 就的一步一步的去求解, 第二种情况, 就是 p[j] <= mx - i, 根据回文串的堆成性质, 可以确定
p[i] = p[j]
当i > mx的时候也是需要我们去一步一步的求解
我在实现的时候只是给出最长回文串的一个,
def commonSubstring(self, s1):
s1 = '#'+'#'.join(s1)+'#'
length = len(s1)
p = [0] * length
i = 0
id, mx = 0, 0
while i < length:
if mx > i:
p[i] = min(mx - i, p[2 * id - i])
else:
p[i] = 1
while (i - p[i] >= 0) and (i + p[i] < length) and (s1[i - p[i]] == s1[i + p[i]]):
p[i] += 1
if (i - 1 + p[i]) > mx:
mx = i -1 + p[i]
id = i
i += 1
max_length = max(p)
index_id = p.index(max_length)
s = s1[index_id + 1 - max_length:index_id + max_length]
s2 = ''
for i in s.split('#'):
s2 += i
return s2
这个是我的实现, 终于明白了这个神奇的算法