Manacher
创建时间:2025-10-05
Manacher 是一种快速解决回文子串问题的算法,通常可以以 \(O(|s|)\) 的时间复杂度完成问题。
注意到回文串的长度可能是奇数也可能是偶数,如果是奇数,那么回文串一定存在一个字符为其对称中心,可以枚举对称中心求解。如果是偶数对称中心在两个字符中间,无法使用上述做法。为了方便操作,Manacher 算法的第一步就是将两种情况统一。
下简称长度为奇数的回文串为奇回文串,长度为偶数的回文串为偶回文串(这种叫法并不标准)。
设有字符串 \(s\),不妨在 \(s\) 的首字符前、末字符后和每两个字符中间加入一个分隔字符(分隔字符不能出现 \(s\) 中)。形式化的说,定义 \(t\) 为操作后的字符串,则 \(t=c_0s_1c_0s_2c_0\cdots c_0s_{|s|}c_0\),其中 \(\forall i\in [1,|s|],c_0\neq s_i\)。这样,任意一个 \(s\) 中奇到 \(t\) 中一个以 \(s_i\) 为对称中心、以 # 为端点的奇回文串,一个 \(s\) 中偶回文串就可以对应到 \(t\) 中一个以 \(c_0\) 为对称中心、以 # 为端点的奇回文串。
这样便将 \(s\) 中奇回文串和偶回文串统一转成 \(t\) 中的以 # 为端点的奇回文串,同样的,\(t\) 中以 # 为端点的任意奇回文串都可以对应到 \(s\) 中的一个奇回文串或偶回文串。设一个奇回文串的长度为 \(len\)(即 \(len \equiv 1 \pmod 2\))则称其半径为 \(\lfloor \frac {len} 2 \rfloor\)。易证,\(s\) 中回文串的长度恰好为其在 \(t\) 中对应的奇回文串的半径。
举个例子,设有 \(s\) 中奇回文串 ab、aba,则其分别对应 \(t\) 中回文串 #a#b#、#a#b#a#。
现在只需要求出 \(t\) 中以 # 为端点的奇回文串即可。
首先考虑暴力算法,枚举对称中心,再暴力向两端扩展,得到以 \(t_i\) 为对称中心的奇回文串的半径最大值,也就是以 \(s_j\) 或 \(s_j\) 和 \(s_{j+1}\) 中间为对称中心的回文串的长度最大值。根据长度最大值可以计算出回文串数量或者其他信息。
显然,暴力算法的时间复杂度为 \(O(|s|^2)\),这个复杂度在数据范围较大时并不能接受,考虑怎么优化。
定义 \(p_i\) 表示以 \(t_i\) 为对称中心的奇回文串的半径最大值 \(+1\),即最大的 \(r\) 满足 \(p_{i-r+1}=p_{i+r-1}\)。现计算 \(p_i\) 的值。
设 \(R=\max_{j \in [1,i)} \{i+p_i\}\),\(mid=\) 其所对应的 \(j\)。若 \(i<R\),则 \(i\) 一定在以 \(t_{mid}\) 为对称中心的最大奇回文串中,并且 \(i\) 在以 \(t_{mid}\) 为对称中心的最大奇回文串中与 \(t_{mid-(i-mid)}=t_{mid \times 2-i}\) 相对应,根据对称性,\(p_i\) 至少为 \(\min(p_{mid \times 2-i},R-i)\);否则,\(i\) 不在以 \(t_{mid}\) 为对称中心的最大奇回文串中,只能确定 \(p_i \ge 1\)。得到 \(p_i\) 的下界后再暴力向两边扩展即可。只要记得更新 \(R\) 和 \(mid\) 就能以较快的速度解决问题。代码如下:
void manacher(string s) {
string t("@#"); // 开头插入哨兵字符,防止扩展过程中越界
for (char ch : s)
t += ch, t += '#';
t += '$'; // 结尾插入哨兵字符,防止扩展过程中越界
int R = 0, mid;
for (int i = 1; i <= s.size() * 2 + 1; i++) {
if (i < R)
p[i] = min(p[mid * 2 - i], R - i);
else p[i] = 1;
while (t[i - p[i]] == t[i + p[i]]) // 暴力扩展
p[i]++;
if (i + p[i] > R)
R = i + p[i], mid = i;
}
}
那么这样做的时间复杂度如何呢?注意到算法的主要时间开销在与暴力向外扩展的部分,考虑什么时候会 \(i\) 会在 \(p_i\) 已有基础值外扩展。
当 \(i \ge R\) 时 \(p_i\) 可能会向外扩展,但显然在扩展结束后 \(R\) 一定会变大,且等于 \(i+p_i\),即至少变大了 \(i\) 的扩展次数;当 \(i<R\),若 \(t_{i-\min(p_{mid \times 2 - i},R-i)} \neq t_{i-\min(mimd \times 2 - i,R-i)}\),\(i\) 一定不会扩展;否则,一定有 \(p_i > p_{mid \times 2-i}\) 或 \(i+p_i> R\)。假设 \(i+p_i < R\),则根据对称性 \(i\) 一定不会扩展(可以画图证明),假设不成立。故 \(i+p_i \ge R\),即 \(i\) 到 \(R-1\) 都是回文串的一部分,\(p_i\) 至少为 \(R-i\),根据对称性,\(p_{mid \times 2-i}\) 也应该至少为 \(R-i\),所以 \(\min(p_{mid \times 2 - i},R-i)=R-i\),所以 \(i\) 每扩展一次 \(R\) 便增加 \(1\)(同 \(i \ge R\) 的情况)。显而易见的是,\(R\) 是单调不减的,且最大值为 \(|t|+1\),故算法总的时间复杂度是线性的,即 \(O(|s|)\)。

浙公网安备 33010602011771号