Manacher’s Algorithm(马拉车算法)用来解决最长回文子串等一系列问题。
Mancher 思路
给定两个变量,当前回文子串右侧最优边界下标R和R所对应的中心下标C,R和C伴生关系,即一一对应,同时更新。当前来到的字符位置为i,然后分几种情况考虑:
- 如果 i > R,则暴力向右扩,无任务优化;
- 如果 i <= R,则继续分三种情况考虑,这里先设定设定两个变量L和i’,分别为R和i关于C的镜像位置:
- 如果以i’为中心的回文子串在[L,R]内,则已以i为中心的回文子串可直接推出;
- 如果以i’为中心的回文子串有一部分跑到了[L,R]外,那么可推断出以i为中心的回文子串为[R’, R];
- 如果以i’为中心的回文子串正好压到了[L,R]线上,那么以i为中心的回文子串有一部分是确定的,即:[R’, R],那会不会扩到更远呢,需要继续判断s[R’-1]是否等于s[R+1],类推;
代码实现
class LongestPalindrome(object):
"""
5. 最长回文子串
https://leetcode-cn.com/problems/longest-palindromic-substring/
"""
def solution(self, s: str) -> str:
"""
时间复杂度:O(n^2),两层循环
空间复杂度:O(1)
:param s:
:return:
"""
start, end = 0, 0
for i in range(len(s)):
left1, right1 = self.expandAroundCenter(s, i, i)
left2, right2 = self.expandAroundCenter(s, i, i+1)
if right1 - left1 > end - start:
start, end = left1, right1
if right2 - left2 > end - start:
start, end = left2, right2
return s[start:end+1]
def expandAroundCenter(self, s, left, right):
while left >= 0 and right < len(s) and s[left] == s[right]:
left -= 1
right += 1
return left+1, right-1
def solution2(self, s):
"""
马拉车算法
Manacher‘s Algorithm
:param s:
:return:
"""
t = self.preProcess(s)
n = len(t)
p = [0]*n
C, R = 0, 0
for i in range(1, n-1):
i_mirror = 2 * C - i
if R > i:
p[i] = min(R-i, p[i_mirror])
else:
p[i] = 0
while t[i+1+p[i]] == t[i-1-p[i]]:
p[i] += 1
if i + p[i] > R:
C = i
R = i + p[i]
max_len = 0
center_idx = 0
for i in range(1, n-1):
if p[i] > max_len:
max_len = p[i]
center_idx = i
start = (center_idx - max_len) // 2
return s[start:start+max_len]
def preProcess(self, s):
if not s:
return "^$"
ret = "^"
for c in s:
ret += "#" + c
ret += "#$"
return ret