Problem:Longest Palindromic Substring
Question
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
思路
Manacher算法
对于一个字符串,除了直接遍历以外,也可以用扩展的方式来找字符串
怎么扩展呢, 以 ababa 为例子
char: a b a b a
pos : 0 1 2 3 4
- 遍历这个字符串,在每个位置,依次向两边拓展,如果不超出边界,而且是回文的则继续拓展直到碰到边界或者不能回文为止。如果越界或者不回文,那就拓展下一个位置
- 字符串的“空隙”(两个字符之间的位置)也是必须拓展的
- 记录遍历期间拓展到的最长的长度和位置,就可以得到最长的回文字符串
比如在 1 位置上,b字母向左右两边拓展,可以得到aba,是回文的,继续拓展,左边会越界,所以就暂停拓展,这个位置最长的回文字符串就是 “aba”。但容易发现,遍历的时候有些子串会被遍历超过两次。
比如在 1 位置和 2 位置,左边的“aba”会被遍历两次(2位置的最长回文串是ababa,可想而知必须拓展到 0 位置的a)。降低算法效率。
另外,这个字符串的“空隙”还要自己去定义,存在奇偶性问题。
Manacher算法解决了这两个缺陷
Manacher算法主要做的事情:
- 在字符串的首尾和空隙插入没有在原串中出现的相同的字符,使得新的字符串是奇数长度
- 维护一个回文半径数组,解决重复访问的问题。
- 记录当前可回文字符串中最右的位置Maxright以及对应的对称轴的位置pos
举个栗子
ababa -> #a#b#a#b#a#
char: # a # b # a # b # a #
RL[]: 1 2 1 3 1 6 1 3 1 2 1
// 回文半径是当前位置可以看成可拓展的最长长度加一
这样带来的好处有:
- 不影响原字符串回文,而且用同样的字符代替空隙,解决对称轴的问题
- 解决重复访问的问题,提高效率
Maxright 和 pos有什么作用?
看下图,也就是解决重复访问问题
具体的讲解,可以到这个专栏看一下
解题代码
class Solution(object):
def longestPalindrome(self, s):
s = '#' + '#'.join(s) + '#'
pos = 0
maxright = 0
RL = [0] * len(s)
maxIndex = 0
maxlen = 0
for i in range(len(s)):
if i < maxright:
RL[i] = min(RL[2*pos-i], maxright - i)
else:
RL[i] = 1
start = i - RL[i]
end = i + RL[i]
while start >= 0 and end < len(s) and s[start] == s[end]:
RL[i] += 1
start = i - RL[i]
end = i + RL[i]
if i + RL[i] - 1 > maxright:
maxright = i + RL[i] - 1
pos = i
if RL[i] > maxlen:
maxlen = RL[i]
maxIndex = i
maxright = maxIndex + RL[maxIndex] - 1
maxleft = 2*maxIndex - maxright
return ''.join(s[maxleft : maxright].split('#'))