Manacher
给出一个只由小写英文字符\(a,b,c,\dots,y,z\)组成的字符串\(S\),求\(S\)中最长回文串的长度,字符串长度为\(n\)
对于一个回文串
①当长度\(len\)是偶数时,关于最中间的两个字母对称
②当长度\(len\)是奇数时,关于最中间的一个字母对称
因此,进行统一,字符串首尾和每个字符间插入一个字符,使得长度为\(2len + 1\)
那么就变成了第②种情况
定义:
p[i] 表示以i为中心的最长回文半径,则p[i] - 1就是最长回文半径
设mx表示回文串中心为id,半径为\(p[id]\)的回文串的最右边
那么在\([id - p[id], id]\)和\([id,id + p[id]]\)是关于id对称的串,那么进行遍历
设i,j是关于id对称的,那么对于回文串在\([id - p[id],p[id] + id]\)这个区间都是对称的,满足\(p[i] = p[j]\),即\(p[i] = p[2 * id - i]\)但如果\(p[j]\)的回文串超出了最左边部分,那么p[i]的回文串半径是\(m - i\),那么\(p[i] = min(p[2 * id - i], mx - i)\)
传送门
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N = 11e6 + 5;
int n,p[N << 1],ans;
char a[N],s[N << 1];
void change() {
s[0] = '@';
s[1] = '#';
for(int i = 0; i < n; i++) {
s[i * 2 + 2] = a[i];
s[i * 2 + 3] = '#';
}
n = n * 2 + 2;
s[n] = '$';
}
void manacher() {
int mx = 0, mid;
for(int i = 1; i <= n; i++) {
if(i < mx)
p[i] = min(p[mid * 2 - i], mx - i);
else p[i] = 1;
while(s[i - p[i]] == s[i + p[i]]) p[i]++;
if(i + p[i] > mx) {
mx = i + p[i];
mid = i;
}
}
}
int main() {
scanf("%s",a);
n = strlen(a);
change();
manacher();
for(int i = 0; i < n; i++)
ans = max(ans, p[i]);
printf("%d",ans - 1);
return 0;
}
I‘m Stein, welcome to my blog