[LuoguP4287][SHOI2011]双倍回文(回文自动机)

[LuoguP4287][SHOI2011]双倍回文(回文自动机)

题面

定义一个字符串为"双倍回文",当且仅当它是回文串,长度为4的倍数,且前一半和后一半的字符串都是回文串。如\(\texttt{abbaabba}\)

给出一个字符串\(S\),求它的最长双倍回文子串的长度。

\(|S|\leq 5 \times 10^5\)

分析

对于PAM上的每个节点,我们维护\(trans\),代表小于等于当前节点串长度一半的最长回文后缀,指向对应后缀的节点.求法和fail基本一样。

int y=t[p].trans;
while(s[pos-len(y)-1]!=s[pos]||(len(y)+2)*2>len(cur)) y=fail(y);
t[cur].trans=t[y].ch[c]; 
//和getfail类似,注意转移过来加了c之后长度为len(y)+2 

然后对于每个节点,判断\(2len(trans(i))\)是否等于当前长度\(len(i)\),且\(4|len(i)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring> 
#define maxn 500000
#define maxc 26
using namespace std;
char s[maxn+5];
struct PAM{
#define fail(x) (t[x].fail)
#define len(x) (t[x].len)
	struct node{
		int ch[maxc];
		int fail;
		int len;
		int trans;
	}t[maxn+5];
	int ptr,last;
	void ini(){
		ptr=1;
		t[0].fail=1;
		t[1].fail=0;
		len(0)=0;
		len(1)=-1;
		last=0;
	} 
	inline int get_fail(int x,int n){
		while(s[n-len(x)-1]!=s[n]) x=fail(x);
		return x;
	}
	void insert(int c,int pos){
		int p=get_fail(last,pos);
		if(t[p].ch[c]==0){
			int cur=++ptr;
			len(cur)=len(p)+2;
			fail(cur)=t[get_fail(fail(p),pos)].ch[c];
			t[p].ch[c]=cur;
			if(len(cur)<=2) t[cur].trans=fail(cur);
			else{
				int y=t[p].trans;
				while(s[pos-len(y)-1]!=s[pos]||(len(y)+2)*2>len(cur)) y=fail(y);
				t[cur].trans=t[y].ch[c]; 
				//和getfail类似,注意转移过来加了c之后长度为len(y)+2 
			}
		}
		last=t[p].ch[c];
	}
	int calc(){
		int ans=0;
		for(int i=2;i<=ptr;i++){
			if(len(t[i].trans)*2==len(i)&&len(i)%4==0) ans=max(ans,len(i));
		}
		return ans;
	}
}T;
int n;
int main(){
	scanf("%d",&n);
	scanf("%s",s+1);
	T.ini();
	for(int i=1;i<=n;i++) T.insert(s[i]-'a',i);
	printf("%d\n",T.calc());
}

posted @ 2020-03-01 20:54  birchtree  阅读(139)  评论(0编辑  收藏  举报