Manacher 算法详解:线性时间求解最长回文子串
在字符串处理问题中,寻找最长回文子串是一个经典场景。虽然中心扩展法直观易懂,但其时间复杂度为 O(n²),在处理大规模数据时显得力不从心。Manacher(马拉车)算法通过巧妙的预处理和对称性优化,将时间复杂度降低至 O(n),是解决此类问题的利器。
核心问题定义
我们需要解决两个关联问题:
- 找出字符串中所有的回文子串。
- 找出其中长度最长的一个。
这两个目标可以通过同一套逻辑同时达成。
基础概念与预处理
什么是回文?
正读和反读都一样的字符串称为回文串。根据长度奇偶性,可分为奇回文串(如 "aba")和偶回文串(如 "abba")。为了统一处理,我们引入以下概念:
- 回文中心:回文串最中间的位置。
- 回文半径:从中心到回文串端点的距离(包含中心本身)。
预处理:统一奇偶性
中心扩展法需要分别处理奇数和偶数长度的情况,这增加了代码复杂度。Manacher 算法的核心技巧之一是在字符之间插入特殊分隔符(通常用 #),将所有回文串转化为奇数长度。
例如,原串 abcbaa 经过处理后变为 #a#b#c#b#a#a#。
- 原奇回文
bab->#b#a#b# - 原偶回文
abba->#a#b#b#a#
这样处理后,所有回文串的中心都落在某个字符上,无需区分奇偶,只需枚举所有中心点即可。
中心扩展法回顾
在理解 Manacher 之前,先回顾一下基础的暴力解法:以每个位置为中心向两边扩展。
string t, s;
int n;
// 预处理函数
void init(const string& str) {
s = "#";
for (char ch : str) {
s += ch;
s += '#';
}
n = s.size();
}
// 中心扩展求最长半径
int expand(int center) {
int d = 1;
while (center - d >= 0 && center + d < n && s[center - d] == s[center + d]) {
d++;
}
return d - 1; // 实际回文长度相关值
}


