Manacher 算法
学习视频
\(O(n)\) 求最长回文字符串
1.将原字符串转化
- 记得\(ms,mp\)的长度要开到原数组的两倍
- \(s\)数组为字符串,\(ms\)为转化后的字符串
- 在\(0\)位置插入边界\(\$\),在结尾位置插入\(0\)
- 其他情况,每个原字符串中的字母都用两个\(\#\)来包围
int l = 0;
ms[l ++] = '$';
ms[l ++] = '#';
for(int i = 0;i < len; ++i) {
ms[l ++] = s[i];
ms[l ++] = '#';
}
ms[l] = 0;
2.求最长回文半径
- 这个回文半径是对于\(mp\)数组而言的,对于原数组\(str\),\(mp[i] - 1\),就是原数组的回文长度
- \(mx\) 代表以\(id\) 为中心的回文串长度的右边界
- \(mp\) 数组代表了以\(i\) 为中心的回文串半径
- 因为以\(id\) 为中心的回文串在$ i< id+mp[id] $ 的情况下,
- 一定可以找到\(mx-i,2\times id - i\)这两个串,他们都在\(id\)的回文半径内
- \(i + j = 2\times id\),所以\(j = 2\times id - i\)
- 当更新了\(mp[i]\)之后,尝试扩展\(mp[i]\)的半径,\(while\)循环即可
- 最后更新当前的\(id,mx\)
int mx = 0,id = 0;
for(int i = 0;i < l; ++i) {
mp[i] = mx > i ? min(mp[2 * id - i],mx - i) : 1;
while(ms[i + mp[i]] == ms[i - mp[i]]) mp[i] ++;
if(i + mp[i] > mx) {
mx = i + mp[i];
id = i;
}
}
模板题
#include <iostream>
#include <cstring>
using namespace std;
#define endl '\n'
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define lowbit(x) ((x)&(-x))
typedef long long ll;
typedef double db;
typedef pair<int,int> PII;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
char ms[N << 1],str[N];
int mp[N << 1];
void manacher(char s[],int len) {
int l = 0;
ms[l ++] = '$';
ms[l ++] = '#';
for(int i = 0;i < len; ++i) {
ms[l ++] = s[i];
ms[l ++] = '#';
}
ms[l] = 0;
int mx = 0,id = 0;
for(int i = 0;i < l; ++i) {
mp[i] = mx > i ? min(mp[2 * id - i],mx - i) : 1;
while(ms[i + mp[i]] == ms[i - mp[i]]) mp[i] ++;
if(i + mp[i] > mx) {
mx = i + mp[i];
id = i;
}
}
}
int main() {
IO;
int cnt = 1;
while(cin >> str) {
if(strcmp(str,"END") == 0) break;
cout << "Case " << cnt ++ << ": ";
int len = strlen(str),ans = 0;
manacher(str,len);
for(int i = 1;i <= len * 2; ++i) ans = max(ans,mp[i]);
cout << ans - 1 << endl;
}
return 0;
}