2018.12.15 bzoj3676: [Apio2014]回文串(后缀自动机)

时间:2023-03-09 00:06:09
2018.12.15 bzoj3676: [Apio2014]回文串(后缀自动机)

传送门

对原串建立一个后缀自动机,然后用反串在上面匹配。

如果当前匹配的区间[l,r][l,r][l,r]包裹了当前状态的endposendposendpos中的最大值,那么[l,maxpos][l,maxpos][l,maxpos]就是一个回文串。

于是赶快码一波统计答案(很遗憾会wa)

因为有可能原串在答案中。

于是我们可以遍历其linklinklink链一直跳来更新答案。

代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int N=6e5+5;
int n;
char s[N];
struct SAM{
	int len[N],son[N][26],link[N],siz[N],pos[N],cnt[N],rk[N],tot,rt,last;
	bool vis[N];
	SAM(){tot=rt=last=1,len[0]=-1;for(ri i=0;i<26;++i)son[0][i]=0;}
	inline void expand(int x,int Pos){
		int p=last,np=++tot;
		siz[last=np]=1,pos[np]=Pos,len[np]=len[p]+1;
		while(p&&!son[p][x])son[p][x]=np,p=link[p];
		if(!p){link[np]=rt;return;}
		int q=son[p][x],nq;
		if(len[q]==len[p]+1){link[np]=q;return;}
		len[nq=++tot]=len[p]+1,memcpy(son[nq],son[q],sizeof(son[q])),link[nq]=link[q];
		while(p&&son[p][x]==q)son[p][x]=nq,p=link[p];
		link[np]=link[q]=nq;
	}
	inline void topsort(){
		for(ri i=1;i<=tot;++i)++cnt[len[i]];
		for(ri i=2;i<=last;++i)cnt[i]+=cnt[i-1];
		for(ri i=1;i<=tot;++i)rk[cnt[len[i]]--]=i;
		for(ri i=tot;i;--i)siz[link[rk[i]]]+=siz[rk[i]],pos[link[rk[i]]]=max(pos[link[rk[i]]],pos[rk[i]]);
	}
	inline void query(){
		ll ans=0;
		int p=1,nowlen=0;
		for(ri i=n;i;--i){
			while(p!=rt&&!son[p][s[i]-'a'])p=link[p],nowlen=len[p];
			if(son[p][s[i]-'a'])++nowlen,p=son[p][s[i]-'a'];
			if(pos[p]<i+nowlen){
				if(i<=pos[p])ans=max(ans,(ll)(pos[p]-i+1)*siz[p]);
				for(ri nowp=link[p];nowp&&!vis[nowp];nowp=link[nowp]){
					vis[nowp]=1;
					if(i<=pos[nowp]&&i+len[nowp]-1>=pos[nowp])ans=max(ans,(ll)(pos[nowp]-i+1)*siz[nowp]);
				}
			}
		}
		cout<<ans;
	}
}sam;
int main(){
	scanf("%s",s+1),n=strlen(s+1);
	for(ri i=1;i<=n;++i)sam.expand(s[i]-'a',i);
	sam.topsort(),sam.query();
	return 0;
}