学习了一下后缀自动机转后缀树的方法。虽然可能这道题目后缀数组也可以做。
考虑后缀自动机的parent,它对应的是比x的right集合略大一点的那个节点;对应到后缀树上,可以发现在缩边后对应的就是y的父亲。
但是后缀自动机求得实际上是所有前缀倒过来后的后缀树;因此把原串反过来跑sam,然后x连向fa[x]就是后缀树了;每个点的len实际上就是这个节点的深度。
然后考虑这道题目,本质就是求两两的lcp的长度,那就对每个后缀树上的节点求一下就好了。
AC代码如下:
#include<bits/stdc++.h>
#define ll long long
#define N 1000005
using namespace std;
int n; char s[N];
struct sam{
int cnt,last,ch[N][26],len[N],fa[N],fst[N],nxt[N],sz[N];
ll ans;
sam(){ cnt=last=1; }
void ins(int c){
int p=last,np=last=++cnt; len[np]=len[p]+1; sz[cnt]=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 (len[p]+1==len[q]) fa[np]=q; else{
int nq=++cnt; len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof(ch[q]));
fa[nq]=fa[q]; fa[np]=fa[q]=nq;
for (; p && ch[p][c]==q; p=fa[p]) ch[p][c]=nq;
}
}
}
void dfs(int x){
int y;
for (y=fst[x]; y; y=nxt[y]){
dfs(y);
ans+=(ll)len[x]*sz[x]*sz[y]; sz[x]+=sz[y];
}
}
void solve(){
int i;
for (i=2; i<=cnt; i++){
nxt[i]=fst[fa[i]]; fst[fa[i]]=i;
}
dfs(1);
printf("%lld\n",((ll)n*(n-1)*(n+1)>>1)-(ans<<1));
}
}sam;
int main(){
scanf("%s",s+1); n=strlen(s+1);
int i;
for (i=n; i; i--) sam.ins(s[i]-'a');
sam.solve();
return 0;
}
by lych
2017.3.16