[bzoj 4650][NOI 2016]優秀的拆分
阿新 • • 發佈:2019-01-12
傳送門
Description
如果一個字串可以被拆分為\(AABB\) 的形式,其中$ A$和 \(B\)是任意非空字串,則我們稱該字串的這種拆分是優秀的。
例如,對於字串\(aabaabaa\),如果令\(A=aab\),\(B=a\),我們就找到了這個字串拆分成 \(AABB\)的一種方式。
一個字串可能沒有優秀的拆分,也可能存在不止一種優秀的拆分。比如我們令\(A=a\),\(B=baa\),也可以用 \(AABB\)表示出上述字串;但是,字串 \(abaabaa\) 就沒有優秀的拆分。
現在給出一個長度為 \(n\)的字串\(S\),我們需要求出,在它所有子串的所有拆分方式中,優秀拆分的總個數。這裡的子串是指字串中連續的一段。
以下事項需要注意:
- 出現在不同位置的相同子串,我們認為是不同的子串,它們的優秀拆分均會被記入答案。
- 在一個拆分中,允許出現\(A=B\)。例如 \(cccc\) 存在拆分\(A=B=c\) 。
- 字串本身也是它的一個子串。
Solution
\(st[i]\)表示以\(i\)開始的有多少個形式如\(aa\)的子串
\(en[i]\)表示以\(j\)結束的有多少個形式如\(aa\)的字串
那麼答案就是\(\sum_{i=1}^{n-1} en[i]*st[i+1]\)
怎麼求這個東西呢?只考慮\(st\),因為把原字串倒個序就能求\(en\)
了列舉\(aa\)中\(a\)的長度,然後每次我們只考慮\([ka+1,(k+1)a]\)這個區間上有多少個可以作為起始點的,它一定包含\((k+1)a\)這個位置,而後面與它相同的字串一定包括\((k+2)a\)這個位置,所以直接查詢\(LCP((k+1)a,(k+2)a)\),以及\(LCS((k+1)a,(k+2)a)\),就可以啦。
根據調和級數和\(SA\),總複雜度是\(O(n\log n)\)
Code
#include<bits/stdc++.h> #define ll long long #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return x*f; } #define MN 30005 #define M(a) memset(a,0,sizeof a); char s[MN]; int lg[MN],en[MN],st[MN],n; void init(){n=strlen(s+1);M(st);M(en);} class SA { private: int height[MN][19],p,q,sa[2][MN],rk[2][MN],num[27]; public: SA(){M(height);M(sa);M(rk);M(num);} inline void init(){M(height);M(sa);M(rk);M(num);} inline void build_sa() { register int i,j,k,mx; for(i=1;i<=n;++i) num[s[i]-'a'+1]++; for(i=1;i<=26;++i) num[i]+=num[i-1]; for(i=1;i<=n;++i) sa[1][num[s[i]-'a'+1]--]=i; for(i=1;i<=n;++i) rk[1][sa[1][i]]=rk[1][sa[1][i-1]]+(s[sa[1][i-1]]!=s[sa[1][i]]); mx=rk[1][sa[1][n]]; for(p=1,q=0,k=1;k<=n;k<<=1,p^=1,q^=1) { if(mx==n) break; for(i=1;i<=n;++i) num[rk[p][sa[p][i]]]=i; for(i=n;i;--i) if(sa[p][i]>k) sa[q][num[rk[p][sa[p][i]-k]]--]=sa[p][i]-k; for(i=n-k+1;i<=n;++i) sa[q][num[rk[p][i]]--]=i; for(i=1;i<=n;++i) rk[q][sa[q][i]]=rk[q][sa[q][i-1]]+(rk[p][sa[q][i]]!=rk[p][sa[q][i-1]]||rk[p][sa[q][i]+k]!=rk[p][sa[q][i-1]+k]); mx=rk[q][sa[q][n]]; } for(i=k=1;i<=n;++i) { if(rk[p][i]==1) continue;if(k) k--; for(j=sa[p][rk[p][i]-1];j+k<=n&&i+k<=n&&s[i+k]==s[j+k];++k); height[rk[p][i]][0]=k; } for(i=1;i<=18;++i)for(j=n;j>=1&&j>(1<<i);--j) height[j][i]=min(height[j][i-1],height[j-(1<<i-1)][i-1]); } inline int LCP(int x,int y) { x=rk[p][x];y=rk[p][y]; if(x>y) std::swap(x,y); return min(height[y][lg[y-x]],height[x+(1<<lg[y-x])][lg[y-x]]); } }A,revA; int main() { register int T,i,j; for(i=2;i<MN;++i) lg[i]=lg[i>>1]+1; T=read();printf("%d\n",T); while(T--) { memset(s,0,sizeof s); scanf("%s",s+1); init(); A.init();A.build_sa(); for(i=1;i+i<=n;++i) std::swap(s[i],s[n-i+1]); revA.init();revA.build_sa(); register int len,x,y; for(len=1;len<=n;++len)for(i=len,j=len<<1;j<=n;i+=len,j+=len) { x=min(A.LCP(i,j),len);y=min(revA.LCP(n-i+1,n-j+1),len); if(x+y-1<len) continue; st[i-y+1]++;st[i+x-len+1]--; en[j+len-y]++;en[j+x]--; } for(i=1;i<=n;++i) st[i]+=st[i-1],en[i]+=en[i-1]; register ll ans=0; for(i=1;i<n;++i) ans+=1ll*en[i]*st[i+1]; printf("%lld\n",ans); } return 0; }
Blog來自PaperCloud,未經允許,請勿轉載,TKS!