题意:
给定字符串s,对于s中每一种本质不同的子串t,设t出现的次数为k,则贡献为k*(k+1)/2,
要求计算所有本质不同的子串的贡献和。
数据范围:|s|<=1e5
解法:
对串s构造后缀自动机,
标记endpos,建parent树,
对于每个状态x,设sz[x]为x之后的endpos数量,令k=sz[x]
状态x处本质不同的串为len[x]-len[fa],令cnt=len[x]-len[fa]
那么对答案的贡献就是k*(k+1)/2*cnt
树形dp求sz[x],同时对每个状态统计对答案的贡献就行了
总复杂度O(n)
code:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxm=2e6+5;
char s[maxm];
struct SAM{
int ch[maxm][26];
int fa[maxm],l[maxm];//l[]是等价类的最长字符串长度len
int last=1,tot=1;//tot是节点数量
//本题要用的
int head[maxm],nt[maxm],to[maxm],cnt;
int sz[maxm];
ll ans=0;
void add(int x,int y){
cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;
}
void dfs(int x){
for(int i=head[x];i;i=nt[i]){
int v=to[i];
dfs(v);
sz[x]+=sz[v];
}
ans+=1ll*sz[x]*(sz[x]+1)/2*(l[x]-l[fa[x]]);
}
ll solve(){
for(int i=2;i<=tot;i++)add(fa[i],i);//建立parent树
dfs(1);
return ans;
}
//SAM构造
void add(int c){
int p=last,np=++tot;last=np;l[np]=l[p]+1;
for(;p&&!ch[p][c];p=fa[p])ch[p][c]=np;
if(!p)fa[np]=1;
else{
int q=ch[p][c];
if(l[p]+1==l[q])fa[np]=q;
else{
int nq=++tot;l[nq]=l[p]+1;
memcpy(ch[nq],ch[q],sizeof ch[q]);
fa[nq]=fa[q];fa[q]=fa[np]=nq;
for(;ch[p][c]==q;p=fa[p])ch[p][c]=nq;
}
}
sz[np]=1;//标记endpos
}
void init(){
for(int i=1;i<=tot;i++){
memset(ch[i],0,sizeof ch[i]);
fa[i]=l[i]=0;
}
last=tot=1;
}
}S;
signed main(){
scanf("%s",s+1);
int n=strlen(s+1);
for(int i=1;i<=n;i++){
S.add(s[i]-'a');
}
ll ans=S.solve();
cout<<ans<<endl;
return 0;
}