背景:
感觉GDOI会考字符串的题目。
KMP、AC自动机都考过了,后缀数组不想背模板,还是搞一搞manacher吧。
于是就看了一个视频。
于是就搞懂了。
题意:
有一个字符串,求他的最长回文字串的长度。
思路:
manacher模板题。
表示回文树不会。
因为回文字串的长度有奇有偶,所以这个算法的第一个巧妙之处就是在每个字符中间加上#(或者其它无用的字符,但是为了尊重算法发明者的版权,就用#号吧)。
此时,最长回文字串的长度一定为奇数(埋一个坑,继续往下看)。
p[i]表示以i为中心的回文半径(即以i为中心的回文字串的长度/2);
id表示当前能拓展出去的最右边的回文字串的中心;
ma表示当前能拓展出去的最右边的回文字串的最右边的编号。
我们发现,对于你在匹配的一个字符,我们都有可能通过一个回文字串覆盖掉它(即ma>i),此时必然在id*2-i的位置出现一个刚刚好与它一一对应的字符(因为以id为中心,这两个字符左右对称)。——第二个巧妙之处。
那么此时我们必然还存在一个性质,即p[i]=min(p[id*2-i],ma-i)(好好理解,不是很好讲,或者说,我也不会)。
最后,暴力拓展即可。
答案为max(p[i]-1)(想想为什么减1,再看看前面的坑,因为在这个回文字串中,p[i]中包括一些#,并且i位置一定为#时p[i]-1的值最大,但这个#算了两遍,所以要减1)。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char s[11000010],st[22000010];
int p[22000010];
int len,ans=0;
void init()
{
st[0]='!';
st[1]='#';
for(int i=0;i<len;i++)
{
st[2*i+2]=s[i];
st[2*i+3]='#';
}
len=len*2+2;
st[len]='@';
}
void manacher()
{
int id=0,ma=0;
for(int i=1;i<len;i++)
{
p[i]=ma>i?min(p[2*id-i],ma-i):1;
for(;st[i+p[i]]==st[i-p[i]];p[i]++);
if(p[i]+i>ma)
{
ma=p[i]+i;
id=i;
}
ans=max(ans,p[i]-1);
}
}
int main()
{
scanf("%s",s);
len=strlen(s);
init();
manacher();
printf("%d",ans);
}