[APIO2014] 回文串

\(\text{Problem}:\)[APIO2014]回文串

\(\text{Solution}:\)

\(\text{PAM}\) 模板题,但 \(\text{SAM}\) 一样可以做。

考虑回文串的形式:\(\text{...[a(bcb]a)...}\)\([]\)\(()\) 内的子串是相同的,且 \(\text{bcb}\) 是一个回文串。

\(S\) 建出 \(\text{SAM}\),用反串去匹配。设当前状态为 \(x\),最长匹配长度为 \(now\)。对于当前左端点即上面的 \((\),存在 \([(])\) 的形式即可统计答案(注意 \([]\)\(()\) 内的子串是相同的,即不会出现 \([\) 位置大于 \((\) 位置的情况)。记 \(f_{x}\) 表示状态 \(x\) 对应的最大的 \(\text{endpos}\),即对于相同的 \([(])\) 情况只计算一次。这样,如果 \(i+now-1\geq f_{x}\),这个状态就产生了 \((f_{x}-i+1)\times size_{x}\) 的贡献。但是 \(x\) 的所有祖先都可能产生贡献,此时暴跳 \(\text{parent}\) 树的复杂度为 \(O(n^2)\)

对于状态 \(x\),当 \(i+\text{len}(x)-1\geq f_{x}\) 的贡献被计算后,就给 \(x\) 打上标记。有标记的点的最大贡献显然已经被计算过。故跳 \(\text{parent}\) 树时,如果到一个有标记的结点就停止。由于每个结点最多只会被打上一次标记,故总时间复杂度为 \(O(n)\)

\(\text{Code}:\)

#include <bits/stdc++.h>
//#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=600010;
inline int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
}
char s[N];
int n,siz[N],fir[N],book[N]; long long Ans;
int head[N],maxE; struct Edge { int nxt,to; }e[N];
inline void Add(int u,int v) { e[++maxE].nxt=head[u]; head[u]=maxE; e[maxE].to=v; }
struct SAM
{
	int link[N],len[N],ch[N][26];
	int tot,lst;
	inline SAM() { tot=lst=1; link[1]=len[1]=0; memset(ch[1],0,sizeof(ch[1])); }
	inline void Extend(int c)
	{
		int now=++tot;
		int p=lst;
		siz[now]=1;
		while(p && !ch[p][c]) ch[p][c]=now, p=link[p];
		len[now]=len[lst]+1;
		fir[now]=len[now];
		if(!p) { lst=now, link[now]=1; return; }
		int q=ch[p][c];
		if(len[q]==len[p]+1) link[now]=q;
		else
		{
			int nq=++tot;
			fir[nq]=fir[q];
			len[nq]=len[p]+1;
			memcpy(ch[nq],ch[q],sizeof(ch[q]));
			link[nq]=link[q];
			link[q]=link[now]=nq;
			while(p && ch[p][c]==q) ch[p][c]=nq, p=link[p];
		}
		lst=now;
	}
	inline void Renew()
	{
		for(ri int i=1;i<=tot;i++) link[i]=len[i]=0, memset(ch[i],0,sizeof(ch[i]));
		tot=lst=1;
	}
}A;
void DFS(int x)
{
	for(ri int i=head[x];i;i=e[i].nxt)
	{
		int v=e[i].to;
		DFS(v);
		siz[x]+=siz[v];
		fir[x]=max(fir[x],fir[v]);
	}
}
signed main()
{
	scanf("%s",s+1);
	n=strlen(s+1);
	for(ri int i=1;i<=n;i++) A.Extend(s[i]-'a');
	for(ri int i=2;i<=A.tot;i++) Add(A.link[i],i);
	DFS(1);
	int sta=1,now=0;
	for(ri int i=n;i;i--)
	{
		int p=s[i]-'a';
		while(sta && !A.ch[sta][p]) sta=A.link[sta], now=A.len[sta];
		if(!sta) { sta=1; continue; }
		sta=A.ch[sta][p], now++;
		if(i+now-1<fir[sta]) continue;
		if(i+min(now,A.len[sta])-1>=fir[sta] && i<=fir[sta]) Ans=max(Ans,1ll*siz[sta]*(fir[sta]-i+1));
		int q=A.link[sta];
		while(q && !book[q])
		{
			if(i+A.len[q]-1>=fir[q] && i<=fir[q]) Ans=max(Ans,1ll*siz[q]*(fir[q]-i+1));
			book[q]=1, q=A.link[q];
		}
	}
	printf("%lld\n",Ans);
	return 0;
}

后记:更详细的讲解

posted @ 2021-04-01 11:26  zkdxl  阅读(42)  评论(0编辑  收藏  举报