P4287 [SHOI2011]双倍回文

求最长双倍回文子串。
考虑建PAM,我们可以知道最长回文子串,但是如何知道最长双倍回文子串呢?
我们发现如果这个子串是双倍回文子串,那么该子串<=len/2的最长回文子串一定是=len/2的,我们可以再记录一个fail'表示<=len/2的最长回文子串后缀。
求答案的时候枚举每个位置判断fail'的len即可。
对于fail'的求法,类似fail的求法再判断len的限制即可。
还有一种方法,就是建完PAM以后在遍历一遍回文树,并用个桶实时存储当前每个长度的个数。每次看看有没有即可。

或者可以用manacher来实现。

#include<cstdio>
#define N 500010
#define db double
#define ll long long
#define fo(x,a,b) for (int x=(a);x<=(b);x++)
#define fd(x,a,b) for (int x=(a);x>=(b);x--)
using namespace std;
int n,ans=0;
char s[N];

struct PAM {
	int t[N<<1][26],len[N<<1],fail[N<<1],trans[N<<1],tot=1,las;
	
	void init() {
		len[0]=0,fail[0]=trans[0]=1;
		len[1]=-1,fail[1]=0,tot=1,las=1;
	}
	
	int get_fail(int pos,int x) {
		while (s[pos-len[x]-1]!=s[pos])
			x=fail[x];
		return x;
	}
	
	int get_fail(int pos,int x,int son,int lim) {
		while (s[pos-len[x]-1]!=s[pos]||len[t[x][son]]>lim)
			x=fail[x];
		return x;
	}
	
	void ins(int x) {
		int now=get_fail(x,las);
		if (! t[now][s[x]-'a']) {
			len[++tot]=len[now]+2;
			int temp=get_fail(x,fail[now]);
			fail[tot]=t[temp][s[x]-'a'];
			if (len[tot]<=2) trans[tot]=fail[tot];
			else {
				temp=get_fail(x,trans[now],s[x]-'a',len[tot]/2);
				trans[tot]=t[temp][s[x]-'a'];
			}
			t[now][s[x]-'a']=tot;
		}
		las=t[now][s[x]-'a'];
	}
	
	int query() {
		int res=0;
		fo(i,2,tot) {
			if (len[i]%4==0&&len[trans[i]]*2==len[i]) {
				if (len[i]>res) res=len[i];
			}
		}
		return res;
	}
}tr;

int main() {
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	scanf("%d%s",&n,s+1);
	tr.init();
	fo(i,1,n) tr.ins(i);
	ans=tr.query();
	printf("%d\n",ans);
	return 0;
}

posted @ 2021-05-27 20:11  jz929  阅读(58)  评论(0编辑  收藏  举报