P3804 【模板】后缀自动机 (SAM)

题意

给定一个只包含小写字母的字符串 \(S\),

请你求出 \(S\) 的所有出现次数不为 \(1\) 的子串的出现次数乘上该子串长度的最大值。

\(1\leq |S|\leq 10^6\)

解法

因为题目就叫后缀自动机,所以要用到sam.

考虑在每个节点上保存右端点的个数再乘上maxlen即可.

在递推的时候我用的是bfs. 大小要保存到 \(2\) 倍.

my code

#include<bits/stdc++.h>
using namespace std;
class state{
public:
	int link,len;
	map<char,int>nxt;
}st[2000010];
int sz=0,lst=0;
void init(){
	st[0].link=-1;
	st[0].len=0;
	sz++;
} 
int cnt[2000010];
void extend(char c){
	int cur=sz++;
	cnt[cur]++;
	st[cur].len=st[lst].len+1;
	int p=lst;
	while(p!=-1&&st[p].nxt.find(c)==st[p].nxt.end()){
		st[p].nxt[c]=cur;
		p=st[p].link;
	}
	if(p==-1){
		st[cur].link=0;
		lst=cur;
		return;
	}
	int q=st[p].nxt[c];
//	cout<<q<<" "<<p<<endl;
	if(st[q].len==st[p].len+1){
		st[cur].link=q;
		lst=cur;
		return;	
	}
	int nq=sz++;
	st[nq].link=st[q].link;
	st[nq].len=st[p].len+1;
	st[nq].nxt=st[q].nxt;
	st[cur].link=st[q].link=nq;
	while(p!=-1&&st[p].nxt[c]==q){
		st[p].nxt[c]=nq;	
		p=st[p].link;
	//	cout<<p<<endl;
	}
	lst=cur;
}
int n;
string s;
int deg[2000010];
long long dp[2000010];
int be=0,ed=0;
int que[2000010];
int main(){
	cin>>s;
	n=(int)s.size();
	init();
	for(int i=0;i<n;i++){
	//	cout<<s[i]<<endl;
		extend(s[i]);
	//	cout<<s[i]<<endl;
	}
//	cout<<"ok"<<endl;
	for(int i=0;i<sz;i++)if(st[i].link!=-1)deg[st[i].link]++;
	for(int i=0;i<sz;i++)if(deg[i]==0)que[ed++]=i;
//	for(int i=0;i<sz;i++)cout<<cnt[i]<<","<<st[i].len<<","<<st[i].link<<" ";cout<<endl;
	while(be<ed){
		int x=que[be++];
		if(cnt[x]!=n&&cnt[x]!=1){
			dp[x]=1ll*st[x].len*cnt[x];	
		//	cout<<st[x].len<<" "<<cnt[x]<<endl;
		}
		if(st[x].link==-1)continue;
		cnt[st[x].link]+=cnt[x];
		dp[st[x].link]=max(dp[st[x].link],dp[x]);
		deg[st[x].link]--;
		if(deg[st[x].link]==0)que[ed++]=st[x].link;
	}
	long long ans=0;
	for(int i=0;i<sz;i++)ans=max(ans,dp[i]);
	cout<<ans<<endl;
	return 0;
}	
/*inline? ll or int? size? min max?*/
posted @ 2021-07-05 16:44  xyangh  阅读(83)  评论(0)    收藏  举报