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?*/

浙公网安备 33010602011771号