算法学习|Manacher (马拉车)
Manacher 算法(马拉车算法)
找到字符串中的最长回文子串
复杂度 O(n)
核心思想
- 统一处理奇偶长度:预处理字符串,插入分隔符(如
#
),将任意字符串转换为奇数长度。- 例:
"aba" → "#a#b#a#"
- 例:
- 利用对称性:维护一个当前已知的最右回文右边界
right
和对应的中心点center
,通过对称点信息快速计算当前点的回文半径。 - 数组 P:记录每个字符作为中心的回文半径(包含自身),最终最长回文长度 =
max(P) - 1
。
算法步骤
- 预处理字符串:插入分隔符,得到新字符串
T
。 - 初始化变量:
P[i]
:记录以T[i]
为中心的最长回文半径(含自身)。center
和right
:当前最右回文的中心和右边界。
- 遍历每个字符:
- 若
i < right
,利用对称点mirror
初始化P[i]
。 - 中心扩展更新
P[i]
。 - 更新
center
和right
。
- 若
Python实现
def longest_palindrome(s: str) -> str:
# 预处理字符串
T = '#' + '#'.join(s) + '#'
n = len(T)
P = [0] * n # P[i] 表示以T[i]为中心的最长回文半径
center = right = 0 # 当前最右回文的中心和右边界
max_len = 0 # 最长回文长度
max_center = 0 # 最长回文的中心
for i in range(n):
# 利用对称性初始化P[i]
if i < right:
mirror = 2 * center - i
P[i] = min(right - i, P[mirror])
# 中心扩展
a, b = i + P[i] + 1, i - P[i] - 1
while a < n and b >= 0 and T[a] == T[b]:
P[i] += 1
a += 1
b -= 1
# 更新最右回文边界和中心
if i + P[i] > right:
center = i
right = i + P[i]
# 更新最长回文记录
if P[i] > max_len:
max_len = P[i]
max_center = i
# 提取最长回文子串(原字符串中的位置)
start = (max_center - max_len) // 2
end = start + max_len
return s[start:end]
# 示例
s = "babad"
print(longest_palindrome(s)) # 输出 "bab" 或 "aba"
关键步骤解析
1. 预处理字符串
T = '#' + '#'.join(s) + '#'
- 输入
s = "babad"
转换为T = "#b#a#b#a#d#"
。 - 统一处理奇偶长度回文,所有回文子串长度均为奇数。
2. 利用对称性初始化 P[i]
if i < right:
mirror = 2 * center - i # 计算i关于center的对称点
P[i] = min(right - i, P[mirror])
- 镜像点
mirror
:i
关于center
对称的位置mirror = 2*center - i
。 - 初始化逻辑:
- 若
P[mirror]
较短,则P[i] = P[mirror]
(未超出左边界)。 - 若
P[mirror]
较长,则P[i] = right - i
(受限于右边界)。
- 若
3. 中心扩展
while a < n and b >= 0 and T[a] == T[b]:
P[i] += 1
a += 1
b -= 1
- 从初始化的半径外继续扩展,直到字符不匹配。
4. 更新最右边界和中心
if i + P[i] > right:
center = i
right = i + P[i]
- 维护
right
为当前已知的最远回文右边界,center
对应其中心。
复杂度分析
- 时间复杂度:O(n),每个字符最多被扩展两次(对称初始化和实际扩展)。
- 空间复杂度:O(n),用于存储预处理字符串和数组
P
。
应用场景
- 最长回文子串
- 回文子串计数
- 字符串压缩中的回文检测