manacher 算法详解
manacher 算法详解
对应解释网站
Manacher - OI Wiki (oi-wiki.org)
Manacher(马拉车)————O(n)回文子串 - 沉~杉 - 博客园 (cnblogs.com)
背景
简介
Manacher算法是一种用于寻找字符串中最长回文子串的高效算法。它的时间复杂度为O(n),其中n是字符串的长度。该算法由Glenn Manacher在1975年提出,并在后续的研究中被不断完善。
算法思想
Manacher算法的核心思想是利用已经计算出的回文信息来减少重复计算。具体来说,当我们在字符串中找到一个回文串时,可以利用这个回文串的对称性来推断出其他位置可能存在的回文串。
算法步骤
-
预处理:
- 为了简化处理过程,通常会在原字符串的每个字符之间插入一个特殊字符(比如’#'),并在字符串的开始和结束处各添加一个特殊字符。这样做是为了避免边界问题,并且使得回文串既可以是奇数长度也可以是偶数长度。
例如,对于字符串
"abcba"
,预处理后变为"#a#b#c#b#a#"
。在此举个例子:s=‘abbadcacda’先转化成s_new=’KaTeX parse error: Expected 'EOF', got '#' at position 1: #̲a#b#b#a#d#c#a#c…‘与’\0’,是边界)
这样原串中的偶回文(abba)与奇回文(adcacda),变成了(#a#d#d#a#)与(#a#d#c#a#c#d#a#)两个奇回文。
-
定义数组P:
- 定义一个数组
P
,其中P[i]
表示以第i个字符为中心的最大回文半径(不包括中心字符本身)。
- 定义一个数组
例子如下:
-
动态规划:
-
初始化两个变量
C
和R
,其中C
表示当前回文中心的位置,R
表示当前回文右边界的位置。 -
遍历预处理后的字符串,对于每个位置 i:
- 如果
i < R
,那么可以利用对称性,找到i
关于C
的镜像位置mirr = 2 * C - i
,并利用已知的P[mirr]
值来初始化P[i]
的最小值。 - 如果
i + P[i] > R
,则需要从R + 1
开始扩展回文串,直到找到不成立的情况为止。 - 更新
C
和R
的值,如果i + P[i] > R
,则C = i
,R = i + P[i]
。
- 如果
-
-
查找最长回文子串:
- 在遍历过程中,记录下最大的
P[i]
值及其对应的索引,最后根据这个索引和值就可以找出最长的回文子串。
- 在遍历过程中,记录下最大的
示例代码
def manacher(s):
# Preprocess the string to avoid edge cases
t = '#' + '#'.join(s) + '#'
n = len(t)
p = [0] * n
center, right = 0, 0
for i in range(n):
mirror = 2 * center - i
if i < right:
p[i] = min(right - i, p[mirror])
# Attempt to expand palindrome centered at i
while i + p[i] + 1 < n and i - p[i] - 1 >= 0 and t[i + p[i] + 1] == t[i - p[i] - 1]:
p[i] += 1
# If palindrome centered at i expands past right,
# adjust center based on expanded palindrome.
if i + p[i] > right:
center, right = i, i + p[i]
# Find the maximum element in p
max_len, center_index = max((n, i) for i, n in enumerate(p))
return s[(center_index - max_len) // 2: (center_index + max_len) // 2]
# Example usage
print(manacher("abcbabcbabcba")) # Outputs: "abcbabcba"