給定一個字符串 \(S\), 求有多少種將 \(S\) 的子串拆分爲形如 AABB
的拆分方案php
\(|S|\le 30000\) (\(95\%\) 數據 \(|S|\le 2000\))c++
考場上碰見這題直接打95分暴力哈希跑路就完事了吧數組
\(O(n^2)\) 暴力就直接枚舉全部子串看它是否是 AA
型的, 在左右端點處分別標記一下, 而後枚舉斷點把兩邊的方案數乘起來就完事了.優化
考慮優化這個暴力. 咱們枚舉這個 AA
串中 A
的長度 \(l\), 而後每隔 \(l\) 取一個關鍵點, 那麼每一個 AA
串必然會覆蓋兩個關鍵點. 對於每對相鄰的關鍵點 \(a\) 和 \(b\), 咱們計算 \(p=\operatorname{LCP}(S[a:],S[b:])\) 以及 \(s=\operatorname{LCS}(S[:a-1],S[:b-1])\) . 那麼只要 \(p+s\ge l\) 就會有 AA
串出現. 畫畫圖能夠發現 \([a-s,a-(l-p)]\) 均可以是 AA
串的左端點. 直接在差分數組上修改左右端點就能夠了. 注意只能計算同時包含這兩個相鄰的關鍵點的 AA
串, 因此應該和 \([a-l+1,a]\) 取交集. 算完以後前綴和一下按照 \(O(n^2)\) 暴力裏的操做算最終答案就能夠了.ui
#include <bits/stdc++.h> const int MAXN=1e5+10; typedef long long intEx; struct SuffixArray{ char s[MAXN]; int SA[MAXN]; int rank[MAXN]; int stmin[20][MAXN]; int* height=stmin[0]; void Build(); int LCP(int,int); }; SuffixArray pf,sf; int n; int lg[MAXN]; int cnt[MAXN]; char buf[MAXN]; int lcnt[MAXN]; int rcnt[MAXN]; int* x=new int[MAXN]; int* y=new int[MAXN]; int LCP(int,int); int LCS(int,int); int main(){ int T; scanf("%d",&T); for(int i=2;i<MAXN;i++) lg[i]=lg[i>>1]+1; while(T--){ scanf("%s",buf+1); n=strlen(buf+1); for(int i=1;i<=n;i++) pf.s[i]=sf.s[n-i+1]=buf[i]; pf.Build(); sf.Build(); memset(lcnt,0,sizeof(int)*(n+1)); memset(rcnt,0,sizeof(int)*(n+1)); for(int len=1;(len<<1)<=n;len++){ for(int a=1,b;(b=a+len)<=n;a=b){ int p=LCP(a,b); int s=LCS(a-1,b-1); if(p+s>=len){ int l=std::max(a-len+1,a-s); int r=std::min(a-(len-p),a); ++lcnt[l]; --lcnt[r+1]; ++rcnt[l+(len<<1)-1]; --rcnt[r+(len<<1)]; } } } for(int i=1;i<=n;i++){ lcnt[i]+=lcnt[i-1]; rcnt[i]+=rcnt[i-1]; } intEx ans=0; for(int i=1;i<n;i++) ans+=1ll*rcnt[i]*lcnt[i+1]; printf("%lld\n",ans); } return 0; } int LCP(int a,int b){ return pf.LCP(a,b); } int LCS(int a,int b){ return sf.LCP(n-a+1,n-b+1); } int SuffixArray::LCP(int a,int b){ if(a<1||a>n||b<1||b>n) return 0; else if(a==b) return n-a+1; else{ a=rank[a]; b=rank[b]; if(a>b) std::swap(a,b); int p=lg[b-a]; ++a; return std::min(stmin[p][a],stmin[p][b-(1<<p)+1]); } } void SuffixArray::Build(){ int m=127; memset(x,0,sizeof(int)*(n+2)); memset(y,0,sizeof(int)*(n+2)); memset(cnt,0,sizeof(int)*(m+1)); for(int i=1;i<=n;i++) ++cnt[x[i]=s[i]]; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) SA[cnt[x[i]]--]=i; for(int k=1;k<n;k<<=1){ int p=0; for(int i=n-k+1;i<=n;i++) y[++p]=i; for(int i=1;i<=n;i++) if(SA[i]>k) y[++p]=SA[i]-k; memset(cnt,0,sizeof(int)*(m+1)); for(int i=1;i<=n;i++) ++cnt[x[i]]; for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1]; for(int i=n;i>=1;i--) SA[cnt[x[y[i]]]--]=y[i]; std::swap(x,y); x[SA[1]]=1; p=1; for(int i=2;i<=n;i++) x[SA[i]]=(y[SA[i]]==y[SA[i-1]]&&y[SA[i]+k]==y[SA[i-1]+k])?p:++p; if(p>=n) break; m=p; } for(int i=1;i<=n;i++) rank[SA[i]]=i; int k=0; for(int i=1;i<=n;i++){ if(rank[i]==1) continue; if(k) --k; int j=SA[rank[i]-1]; while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k]) ++k; height[rank[i]]=k; } for(int i=1;(1<<i)<=n;i++){ for(int j=2;j<=n;j++){ stmin[i][j]=stmin[i-1][j]; if(j+(1<<(i-1))<=n) stmin[i][j]=std::min(stmin[i][j],stmin[i-1][j+(1<<(i-1))]); } } }