思路
SAM
后缀自动机parent树的LCA就是两个子串的最长公共后缀
现在要求LCP
所以把字符串反转一下
然后每个点的贡献就是endpos的大小,dfs一遍求出贡献就可以了
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
const int MAXN = 500500*2;
int Nodecnt,trans[MAXN][26],suflink[MAXN],endpos[MAXN],maxlen[MAXN],fir[MAXN],nxt[MAXN],v[MAXN],cnt,ans,n;
char s[MAXN];
int sum(int l,int r){
if(r<l)
return 0;
return (r+l)*(r-l+1)/2;
}
void addedge(int ui,int vi){
++cnt;
v[cnt]=vi;
nxt[cnt]=fir[ui];
fir[ui]=cnt;
}
int New_state(int _maxlen,int *_trans,int _suflink){
int o=++Nodecnt;
maxlen[o]=_maxlen;
suflink[o]=_suflink;
if(_trans){
for(int i=0;i<26;i++)
trans[o][i]=_trans[i];
}
return o;
}
int add_len(int u,int c){
int z=New_state(maxlen[u]+1,NULL,0);
endpos[z]=1;
while(u&&(trans[u][c]==0)){
trans[u][c]=z;
u=suflink[u];
}
if(!u){
suflink[z]=1;
return z;
}
int v=trans[u][c];
if(maxlen[u]+1==maxlen[v]){
suflink[z]=v;
return z;
}
int y=New_state(maxlen[u]+1,trans[v],suflink[v]);
suflink[z]=suflink[v]=y;
while(u&&trans[u][c]==v){
trans[u][c]=y;
u=suflink[u];
}
return z;
}
void dfs(int x){
for(int i=fir[x];i;i=nxt[i]){
dfs(v[i]);
ans-=endpos[v[i]]*endpos[x]*maxlen[x]*2;
endpos[x]+=endpos[v[i]];
}
}
signed main(){
scanf("%s",s+1);
n=strlen(s+1);
reverse(s+1,s+n+1);
Nodecnt=1;
int last=1;
for(int i=1;i<=n;i++)
last=add_len(last,s[i]-'a');
for(int i=1;i<=n;i++){
int j=i+1;
ans+=(n-i+1)*(n-j+1)+sum(1,n-j+1);
}
for(int i=2;i<=Nodecnt;i++)
addedge(suflink[i],i);
dfs(1);
printf("%lld
",ans);
return 0;
}