manacher 算法
题目大意:求最长回文子串的长度(题目)
分析:
- 设字符串为\(S\),首先我们要寻找它的对称中心,但发现对称中心可能在一个字符上,也可能在两个字符中间
因为在两个字符中间的我们不好访问,于是我们可以在中间插入一些无关的字符,让字符来代替中间的空隙
- 我们记录一个数组\(P\),\(P_i\)表示以\(S_i\)为中心的回文数列的最大半径
如图,\(P_7=6\),在记录\(mid,r\),表示当前已知的\(P_i\)中,\(P_{mid}+r\)是最大的,即右区间离\(S_{S.length()}\)最近
- 于是对于一个未知的\(P_i\)如果\(i \leqslant P_{mid}+r\)那么就可以根据回文数的对称性,确定在\(mid-r \leqslant i \leqslant {mid}+r\)这段区间里,\(P_i=min(P_{关于mid对称的点},mid+r-i)\),易得对称点为\(2*mid-i\),于是\(P_i=min(P_{2*mid-i},mid+r-i)\)
- 但是我们不知道\(mid+r\)后面是否还有相同的\(P_x,P_y\),所以要暴力枚举,最后更新\(mid,r\)
- 于是最大值就是\(P_{max}\)
代码实现:
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
void read(int &sum)
{
sum=0;char last='w',ch=getchar();
while (ch<'0' || ch>'9') last=ch,ch=getchar();
while (ch>='0' && ch<='9') sum=sum*10+ch-'0',ch=getchar();
if (last=='-') sum=-sum;
}
char s[11000002*2],g[11000002*2];
int p[11000002*2];
int len;
int main()
{
// freopen("M.in","r",stdin);
// freopen("M.out","w",stdout);
s[++len]='|';
char ch=getchar();
while (ch!=EOF)
{
s[++len]=ch;
s[++len]='|';
ch=getchar();
}
int mmax=0;
// for (int i=1;i<=len;i++) printf("%c",s[i]);
for (int i=1,r=0,mid=0;i<=len;i++)
{
if (i<=mid+r) p[i]=min(p[2*mid-i],mid+r-i+1);
while (i-p[i]>=1 && i+p[i]<=len && s[i-p[i]]==s[i+p[i]]) p[i]++;
if (s[i-p[i]]!=s[i+p[i]] || i-p[i]<1 || i+p[i]>len) p[i]--;
if (i+p[i]>r) r=p[i],mid=i;
mmax=max(mmax,p[i]);
}
// printf("\n");
printf("%d",mmax);
// fclose(stdin);fclose(stdout);
return 0;
}