【算法】manacher
1. 算法简介
Manacher 算法,俗称马拉车。是一个可以在线性时间复杂度内高效解决最大回文子串的问题。
2. 算法流程
暴力想必大家也都会,就是枚举中心点然后暴力扩展长度。时间复杂度 \(O(n^2)\)。
还有就是字符串哈希 + 二分:枚举中心点,将暴力的扩展变成二分。因为长度越长更不能回文,长度越短更能回文,满足单调性。判断子串相同就用哈希即可。这样可以做到时间复杂度 \(O(n\log n)\)。
最后是 manacher 算法,就是利用那些已经计算过的信息看来推导没有计算过的信息。
首先处理一下奇回文和偶回文的情况:在字符串的开头、结尾以及每一个字符之间插入一个没有在串中出现过的字符 &
或 $
或 #
,可以发现这样处理过后的字符串的最大回文串的开头结尾均为 "插入字符",原最大回文串长为新串的最大回文半径长度 \(-1\)。
对于每一个位置 \(i\) 维护一个最长回文半径 \(d_i\),并维护一个全局最长回文半径 \(R\) 以及她所在的中心位置 \(M\),然后分类讨论。
- \(i>r\),显然无法利用原有的信息,直接暴力去做。
- \(i\le r\),找到 \(i\) 在全局最长回文串下对应的 \(k=2M-i\) 位置,可以分为两类;
-
- 如果 \(d_k\) 对应的区间被包含于 \([2M-R,R]\) 区间中,则 \(d_k=d_i\);
-
- 如果 \(d_k\) 对应的区间没有被包含于 \([2M-R,R]\) 区间中,则令 \(d_k=R-i+1\) 然后暴力匹配;
根据下图加深理解:
- 如果 \(d_k\) 对应的区间被包含于 \([2M-R,R]\) 区间中,则 \(d_k=d_i\);
- 如果 \(d_k\) 对应的区间没有被包含于 \([2M-R,R]\) 区间中,则令 \(d_k=R-i+1\) 然后暴力匹配;
3. 算法实现
#include<bits/stdc++.h>
#define int long long
#define reg register
#define For(i,l,r) for(reg int i=l;i<=r;++i)
#define FOR(i,r,l) for(reg int i=r;i>=l;--i)
using namespace std;
const int N = 5e7;
int n, d[N];
char s[N];
string t = " ";
int Manacher() {
int ans = 0;
for (reg int i = 1, M = 0, r = 0; i <= n; ++i) {
if(i <= r) d[i] = min(d[2 * M - i], r - i + 1);
while(i - d[i] >= 1 && i + d[i] <= n && t[i - d[i]] == t[i + d[i]]) d[i]++;
if(i + d[i] - 1 > r) M = i, r = i + d[i] - 1;
ans = max(ans, d[i] - 1);
}
return ans;
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> (s + 1);
n = strlen(s + 1);
t += '#';
For(i,1,n) {
t += s[i], t += "#";
}
n = 2 * n + 1;
cout << Manacher() << '\n';
return 0;
}
后记
可以拿 Z 函数和 Manacher 的关键代码进行对比:
Manacher:
for (reg int i = 1, M = 0, r = 0; i <= n; ++i) {
if(i <= r) d[i] = min(d[2 * M - i], r - i + 1);
while(i - d[i] >= 1 && i + d[i] <= n && t[i - d[i]] == t[i + d[i]]) d[i]++;
if(i + d[i] - 1 > r) M = i, r = i + d[i] - 1;
}
Z 函数:
for (reg int i = 2, l, r = 0; i <= m; ++i) {
if(i <= r) z[i] = min(z[i - l + 1], r - i + 1);
while(i + z[i] <= m && t[1 + z[i]] == t[i + z[i]]) z[i]++;
if(i + z[i] - 1 > r) l = i, r = i + z[i] - 1;
}
这俩玩意的代码简直一模一样,相似度 \(85\%\),所以她们俩也很好背。