[BZOJ4650][NOI2016]优秀的拆分(SAM构建SA)

关于解法这个讲的很清楚了,主要用了设关键点的巧妙思想。

主要想说的是一个刚学的方法:通过后缀自动机建立后缀树,再转成后缀数组。

后缀数组功能强大,但是最令人头疼的地方是模板太难背容易写错。用这个方法,只需要用上SAM的模板即可。

https://blog.csdn.net/lvzelong2014/article/details/79006541

反串后缀自动机的parent树就是原串的后缀树,一遍DFS即可求出后缀数组。

这样代码复杂度上可能稍简单些(在忘记SA模板的时候可以用),构建过程的复杂度也由$O(n\log n)$变为线性,但由于这个线性复杂度是非常满的,所以常常会比SA还要慢不少。注意SAM的数组最好开两倍,一倍是肯定不够的。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 #define mem(a) memset(a,0,sizeof(a))
 5 #define LCP(x,y) SA.que(x,y)
 6 #define LCS(x,y) SA1.que(n-y+1,n-x+1)
 7 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
 8 typedef long long ll;
 9 using namespace std;
10 
11 const int N=60010;
12 int n,T,f[N],g[N],log[N];
13 char s[N]; ll ans;
14 
15 struct Suffix{
16     int lst,cnt,np,tot,h[N],pos[N],x[N],b[N],mx[N];
17     int son[N][27],fa[N],ch[N][27],rk[N],sa[N],st[N][16];
18     void init(){ lst=cnt=1; tot=0; mem(b); mem(ch); mem(fa); mem(son); }
19     
20     void ext(int c,int k){
21         int p=lst; lst=np=++cnt; pos[np]=k; b[np]=1; mx[np]=mx[p]+1;
22         while (p && !son[p][c]) son[p][c]=np,p=fa[p];
23         if (!p) fa[np]=1;
24         else{
25             int q=son[p][c];
26             if (mx[q]==mx[p]+1) fa[np]=q;
27             else{
28                 int nq=++cnt; mx[nq]=mx[p]+1; pos[nq]=pos[q];
29                 while (p && son[p][c]==q) son[p][c]=nq,p=fa[p];
30                 memcpy(son[nq],son[q],sizeof(son[nq]));
31                 fa[nq]=fa[q]; fa[q]=fa[np]=nq;
32             }
33         }
34     }
35 
36     void build(){ rep(i,2,cnt) ch[fa[i]][x[pos[i]+mx[fa[i]]]]=i; }
37     void dfs(int x){
38         if (b[x]) sa[rk[pos[x]]=++tot]=pos[x];
39         rep(i,1,26) if (ch[x][i]) dfs(ch[x][i]);
40     }
41     
42     void get(){
43         int k=0;
44         rep(i,1,n){
45             for (int j=sa[rk[i]-1]; i+k<=n && j+k<=n && x[i+k]==x[j+k]; k++);
46             h[rk[i]]=k; if (k) k--;
47         }
48     }
49 
50     void rmq(){
51         rep(i,1,n) st[i][0]=h[i];
52         rep(i,1,log[n])
53         rep(j,1,n-(1<<i)+1) st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
54     }
55 
56     int ask(int l,int r){
57         l++; int t=log[r-l+1];
58         return min(st[l][t],st[r-(1<<t)+1][t]);
59     }
60 
61     int que(int x,int y){ return ask(min(rk[x],rk[y]),max(rk[x],rk[y]));}
62 
63     void build_sa(char s[]){
64         for (int i=n; i; i--) ext(x[i]=s[i]-'a'+1,i);
65         build(); dfs(1); get(); rmq();
66     }
67 }SA,SA1;
68 
69 void solve(){
70     mem(f); mem(g);
71     for (int len=1,x,y,l,r; 2*len<=n; len++)
72         for (int i=1,j=len+1; j<=n; i+=len,j+=len)
73             if (s[i]==s[j]){
74                 x=LCS(i,j); y=LCP(i,j);
75                 l=max(i,i-x+len); r=min(i+y,j);
76                 if (r>l){
77                     f[l+len]++; f[r+len]--;
78                     g[l-len+1]++; g[r-len+1]--;
79                 }
80             }
81     rep(i,2,n) f[i]+=f[i-1],g[i]+=g[i-1];
82     rep(i,1,n-1) ans+=(ll)f[i]*g[i+1];
83 }
84 
85 int main(){
86     freopen("bzoj4650.in","r",stdin);
87     freopen("bzoj4650.out","w",stdout);
88     log[1]=0; rep(i,2,N) log[i]=log[i>>1]+1;
89     scanf("%d",&T);
90     while (T--){
91         SA.init(); SA1.init(); scanf("%s",s+1); n=strlen(s+1);
92         SA.build_sa(s); reverse(s+1,s+n+1);
93         SA1.build_sa(s); reverse(s+1,s+n+1);
94         ans=0; solve(); printf("%lld\n",ans);
95     }
96     return 0;
97 }

 

posted @ 2018-07-06 12:18  HocRiser  阅读(435)  评论(0编辑  收藏  举报