题意
∑len(Ti)+∑len(Tj)
可以
O(1)
计算出来。
主要就是求
lcp(Ti,Tj)
将字符串反过来,建立后缀自动机,parent树就是原串的后缀树,lcp就是对应节点的lca,树形DP。
这幅图来自一篇不错的后缀自动机讲解
#include <cstdio>
#include <iostream>
#include <cstring>
#include <string>
#define N 500010<<1
using namespace std;
typedef long long ll;
struct SAM_{
int next[N][26],fail[N],stp[N],p,cnt,t[N],r[N];
ll f[N],w[N];
SAM_(){p=cnt=1;}
void Extend(int x){
x-='a';
int np=++cnt;stp[np]=stp[p]+1;w[np]=f[np]=1;
while(p&&!next[p][x]) next[p][x]=np,p=fail[p];
if(!p) fail[np]=1;
else{
int q=next[p][x];
if(stp[q]==stp[p]+1) fail[np]=q;
else{
int nq=++cnt;stp[nq]=stp[p]+1;
memcpy(next[nq],next[q],sizeof(next[q]));
fail[nq]=fail[q];
fail[q]=fail[np]=nq;
while(p&&next[p][x]==q) next[p][x]=nq,p=fail[p];
}
}
p=np;
}
void Samort(){
int i;
for(i=0;i<=cnt;i++) t[i]=0;
for(i=1;i<=cnt;i++) t[stp[i]]++;
for(i=1;i<=cnt;i++) t[i]+=t[i-1];
for(i=1;i<=cnt;i++) r[t[stp[i]]--]=i;
}
void Solve(int n){
for(int i=cnt;i;i--)f[fail[r[i]]]+=f[r[i]];
ll Ans1=1ll*n*(n+1)/2*(n-1),Ans2=0;
for(int i=1;i<=cnt;i++){
Ans2+=1ll*w[fail[i]]*stp[fail[i]]*f[i];
w[fail[i]]+=f[i];
}
printf("%lld\n",Ans1-Ans2*2);
}
}SAM;
char A[N];
int n;
int main(){
scanf("%s",A+1);n=strlen(A+1);
for(int i=n;i;i--)
SAM.Extend(A[i]);
SAM.Samort();
SAM.Solve(n);
}