【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 }

猜你喜欢

转载自www.cnblogs.com/xiefengze1/p/11016906.html
今日推荐