[BZOJ4650][NOI2016]優秀的拆分(SAM構建SA)
阿新 • • 發佈:2018-07-06
zoj html ati 常常 pre https 還要 數組 blog
關於解法這個講的很清楚了,主要用了設關鍵點的巧妙思想。
主要想說的是一個剛學的方法:通過後綴自動機建立後綴樹,再轉成後綴數組。
後綴數組功能強大,但是最令人頭疼的地方是模板太難背容易寫錯。用這個方法,只需要用上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 namespacestd; 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 }
[BZOJ4650][NOI2016]優秀的拆分(SAM構建SA)