[BZOJ4650][NOI2016]優秀的拆分(SAM構建SA)

關於解法這個講的很清楚了,主要用了設關鍵點的巧妙思想。html

主要想說的是一個剛學的方法:經過後綴自動機創建後綴樹,再轉成後綴數組。數組

後綴數組功能強大,可是最使人頭疼的地方是模板太難背容易寫錯。用這個方法,只須要用上SAM的模板便可。ui

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

反串後綴自動機的parent樹就是原串的後綴樹,一遍DFS便可求出後綴數組。.net

這樣代碼複雜度上可能稍簡單些(在忘記SA模板的時候能夠用),構建過程的複雜度也由$O(n\log n)$變爲線性,但因爲這個線性複雜度是很是滿的,因此經常會比SA還要慢很多。注意SAM的數組最好開兩倍,一倍是確定不夠的。code

 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 }
相關文章
相關標籤/搜索