给出一个长n的字符集为小写字母的字符串,求∑1≤i<j≤nlen(Suffixi)+len(Suffixj)−2×len(lcp(Suffixi,Suffixj))
Suffixx代表从x开始的后缀,
如果我们拥有这个字符串的后缀树,将每个后缀所对应的节点记为标记点。
对每一个标记点对,我们统计的其实是这两个点到根的距离和减去根到他们LCA的距离的二倍。也就是这两个点之间的路径的距离。
那我们要求的实际上就是树上所有标记点对的距离和。
以下不加证明的给出一个结论
一个字符串翻转的后缀自动机的parent树就是在这个字符串的后缀树上将后缀对应的节点作为关键点所建立的虚树,parent树中子节点和父节点的len的差值就是这两个点在后缀树上的距离。
有了上面那个结论之后我们就可以用SAM建立后缀树,在后缀树上做一下树dp就可以了。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 512345,mlen = 26;
#define LL long long
LL ans;
LL dp[maxn*2],siz[maxn*2];
vector<pair<int,int> >edge[maxn*2];
void Link(int st,int ed,int v){
edge[st].push_back(make_pair(ed,v));
edge[ed].push_back(make_pair(st,v));
}
struct Sam{
int len[maxn*2],fa[maxn*2],nex[maxn*2][mlen];
int val[maxn*2];
int _cnt,root,omg;
int newNode(int L = 0){
len[_cnt] = L;
memset(nex[_cnt],fa[_cnt] = -1,sizeof(nex[_cnt]));
return _cnt++;
}
void init(){
_cnt = 0;
memset(val,0,sizeof(val));
root = omg = newNode();
}
void extend(int x){
int ox = newNode(len[omg]+1);
val[ox]++;
while(omg != -1 && nex[omg][x] == -1){
nex[omg][x] = ox;
omg = fa[omg];
}
if(omg == -1) fa[ox] = root;
else{
int omgx = nex[omg][x];
if(len[omgx] == len[omg]+1) fa[ox] = omgx;
else{
int mgx = newNode(len[omg]+1);
for(int i=0;i<mlen;i++)
nex[mgx][i] = nex[omgx][i];
fa[mgx] = fa[omgx];
fa[omgx] = fa[ox] = mgx;
while(omg != -1 && nex[omg][x] == omgx)
nex[omg][x] = mgx,omg = fa[omg];
}
}
omg = ox;
}
void build(char *arr){
init();
for(int i=0;arr[i];i++){
extend(arr[i] - 'a');
}
}
void treeBuild(){
for(int i=0;i<_cnt;i++){
edge[i].clear();
dp[i] = 0 , siz[i] = val[i];
}
for(int i=0;i<_cnt;i++){
if(fa[i] != -1){
Link(i,fa[i],len[i] - len[fa[i]]);
}
}
}
}SAM;
char arr[maxn];
void dfs(int st,int fa = -1){
for(vector<pair<int,int> >::iterator it = edge[st].begin();
it != edge[st].end();it++){
pair<int,int> x = *it;
if(x.first == fa) continue;
dfs(x.first,st);
ans += dp[st] * siz[x.first] + (dp[x.first] + siz[x.first] * x.second) * siz[st];
dp[st] += dp[x.first] + siz[x.first] * x.second;
siz[st] += siz[x.first];
}
}
int main(){
scanf("%s",arr);
int len = strlen(arr);
reverse(arr,arr+len);
SAM.build(arr);
SAM.treeBuild();
ans = 0;
dfs(SAM.root);
printf("%lld\n",ans);
return 0;
}