[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;
}

posted @ 2017-11-22 16:34  Mrha  阅读(153)  评论(0编辑  收藏  举报