【bzoj3238】差异 后缀树
题目大意:给你一个字符串$S$,设$S_i$是串$S$第$i$长的后缀,求:
$\sum\limits_{i=1}^{|S|} \sum\limits_{j=i+1}^{|S|} |S_i|+|S_j|-2\times lcp(S_i,S_j)$
其中$lcp(x,y)$表示字符串$x$和字符串$y$的最长公共前缀
数据范围:$|S|≤500000$
最近发现后缀树和$sam$没学好,找一点题来做一做
一道后缀树的板题,我们用$sam$建出后缀树后,直接$dfs$遍历,通过$siz$更新$ans$即可,详情见代码
时间复杂度:$O(|S|)$
1 #include<bits/stdc++.h> 2 #define M 1000005 3 #define L long long 4 using namespace std; 5 6 char s[M]={0}; 7 8 struct edge{int u,v,next;}e[M]={0}; int head[M]={0},use=0; 9 void add(int x,int y,int z){use++;e[use].u=y;e[use].next=head[x];head[x]=use; e[use].v=z;} 10 L siz[M]={0},hh[M]={0},n,ans=0; 11 12 namespace sam{ 13 int ch[M][26],fa[M],l[M],use=1,last=1; 14 void exc(int c){ 15 int p=last,np=++use; l[np]=l[p]+1; last=use; hh[np]=1; 16 for(;p&&ch[p][c]==0;p=fa[p]) ch[p][c]=np; 17 if(!p) fa[np]=1; 18 else{ 19 int q=ch[p][c]; 20 if(l[p]+1==l[q]) fa[np]=q; 21 else{ 22 int nq=++use; 23 l[nq]=l[p]+1; fa[nq]=fa[q]; 24 fa[q]=fa[np]=nq; 25 memcpy(ch[nq],ch[q],sizeof(ch[q])); 26 for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=nq; 27 } 28 } 29 } 30 void build(){ 31 for(int i=2;i<=use;i++) add(fa[i],i,l[i]-l[fa[i]]); 32 } 33 }; 34 35 void dfs(int x,int dep){ 36 L sumsq=0; 37 for(int i=head[x];i;i=e[i].next){ 38 dfs(e[i].u,dep+e[i].v); 39 siz[x]+=siz[e[i].u]; 40 sumsq+=siz[e[i].u]*siz[e[i].u]; 41 } 42 ans-=(siz[x]*siz[x]-sumsq)*dep; 43 ans-=hh[x]*dep*siz[x]*2; 44 siz[x]+=hh[x]; 45 } 46 47 main(){ 48 scanf("%s",s+1); n=strlen(s+1); 49 for(int i=n;i;i--) sam::exc(s[i]-'a'); 50 sam::build(); 51 ans=(n-1)*(n+1)*n/2; 52 dfs(1,0); 53 cout<<ans<<endl; 54 }