[bzoj] 2565 最长双回文串
原题
双回文串的定义是“回文串回文串”,所以答案就是以i结尾的最长回文串长度加上以i+1为开头的最长回文串的长度
然而怎么处理作为结尾时的长度和作为开头时的长度呢?n^2肯定是不行的啊!
Eg:求结尾数组
对于当前i结尾的ans,一定越早更新他的越长,所以像manacher一样,记录一个右端点,如果i>maxr那么暴力更新,否则不用更新。而且每个位置只用被更新一遍,也就是O(n)的了
开头数组是同理的
#include<cstdio>
#include<algorithm>
#define N 200010
using namespace std;
int l=1,now,ans,lft[N],rght[N],a[N],mxr,p;
char s[N],c;
int main()
{
s[0]='@';
while (~scanf("%c",&c))
{
if (c=='\n') break;
s[l++]='#';
s[l++]=c;
}
s[l++]='#';
s[l++]='?';
for (int i=1,q;i<=l;i++)
{
if (mxr>i) q=min(mxr-i,a[2*p-i]);
else q=1;
while (s[i-q]==s[i+q]) q++;
a[i]=q;
if (i+q>mxr) mxr=i+q,p=i;
}
for (int i=1;i<=l;i++)
{
if (i+a[i]>now)
{
for (int j=now+1;j<=i+a[i];j++)
if (s[j]!='#') lft[j]=j-i+1;
now=i+a[i]-1;
}
}
now=l-1;
for (int i=l-1;i>=1;i--)
{
if (i-a[i]<now)
{
for (int j=now-1;j>=i-a[i];j--)
if (s[j]!='#') rght[j]=i-j+1;
now=i-a[i]+1;
}
}
for (int i=2;i<=l;i+=2)
if (s[i]!='#') ans=max(ans,lft[i]+rght[i+2]);
printf("%d\n",ans);
return 0;
}