[bzoj4650][Noi2016]優秀的拆分——後綴數組
阿新 • • 發佈:2019-02-07
重疊 數組 urn eve back 字符 e-mail 種子 lcs 分割點的地方,得到了分割點,\(AA\)的起點和終點的範圍也就知道了。
題目大意:
定義一個字符串的拆分是優秀的當且僅當是\(AABB\)的形式,求給定字符串的所有子串的所有的拆分中有多少是優秀的。
思路:
95分太好拿了,這裏直接考慮正解該怎麽做。
不難發現我們只需要求出每個點開頭的\(AA\)形式的字符串和每個點結尾的\(AA\)字符串,然後枚舉分割點兩邊乘起來就好了。可是關鍵是\(AA\)形式的字符串可能有\(n^2\)個,直接枚舉的話一定不是正解。
考慮分長度來處理所有的這種子串,對於長度為\(2\times len\)的\(AA\)形式的子串,我們將原串每隔\(len\)就建立一個關鍵點,不難發現所有長度為\(2\times len\)的子串必定可以表示為兩個關鍵點前面接一個lcs,後面接一個lcp,並且lcs和lcp的部分有重疊,重疊的部分就是可以作為兩個\(A\)
於是我們只需要枚舉每兩個關鍵點,然後實現區間加法,單點查詢即可。
/*======================================= * Author : ylsoi * Time : 2019.2.6 * Problem : luogu1117 * E-mail : [email protected] * ====================================*/ #include<bits/stdc++.h> #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i) #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i) #define debug(x) cout<<#x<<"="<<x<<" " #define fi first #define se second #define mk make_pair #define pb push_back typedef long long ll; typedef unsigned long long ull; using namespace std; void File(){ freopen("luogu1117.in","r",stdin); freopen("luogu1117.out","w",stdout); } template<typename T>void read(T &_){ _=0; T fl=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar())if(ch=='-')fl=-1; for(;isdigit(ch);ch=getchar())_=(_<<1)+(_<<3)+(ch^'0'); _*=fl; } const int maxn=3e4+10; int T,n,Log[maxn]; struct Suffix_Array{ char s[maxn]; int sz,sa[maxn],rk[maxn],tp[maxn],tax[maxn],height[maxn]; int st[maxn][15]; void radix_sort(){ REP(i,1,sz)tax[i]=0; REP(i,1,n)++tax[rk[i]]; REP(i,1,sz)tax[i]+=tax[i-1]; DREP(i,n,1)sa[tax[rk[tp[i]]]--]=tp[i]; } void suffix_sort(){ memset(rk,0,sizeof(rk)); memset(sa,0,sizeof(sa)); memset(tp,0,sizeof(tp)); sz=26; REP(i,1,n)rk[i]=s[i]-'a'+1,tp[i]=i; radix_sort(); for(int w=1,p=0;w<n;w<<=1){ p=0; REP(i,1,w)tp[++p]=n-w+i; REP(i,1,n)if(sa[i]>w)tp[++p]=sa[i]-w; radix_sort(); swap(rk,tp); rk[sa[1]]=p=1; REP(i,2,n) if(tp[sa[i-1]]==tp[sa[i]] && tp[sa[i-1]+w]==tp[sa[i]+w])rk[sa[i]]=p; else rk[sa[i]]=++p; sz=p; if(sz==n)break; } int p=0; REP(i,1,n){ if(p)--p; int j=sa[rk[i]-1]; while(s[i+p]==s[j+p])++p; height[rk[i]]=p; } REP(i,1,n)st[i][0]=height[i]; REP(j,1,Log[n]) REP(i,1,n-(1<<j)+1) st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); } int lcp(int x,int y){ x=rk[x],y=rk[y]; if(x>y)swap(x,y); ++x; int d=Log[y-x+1]; return min(st[x][d],st[y-(1<<d)+1][d]); } }S[2]; ll pre[maxn],nex[maxn]; void work(){ memset(pre,0,sizeof(pre)); memset(nex,0,sizeof(nex)); REP(len,1,n/2){ if(len==1){ REP(i,1,n-1)if(S[0].s[i]==S[0].s[i+1]){ ++pre[i],--pre[i+1]; ++nex[i+1],--nex[i+2]; } continue; } for(int i=len*2;i<=n;i+=len){ int j=i-len; int l=max(j-S[1].lcp(n-j+1,n-i+1)+1,j-len+1); int r=min(j+S[0].lcp(j,i)-len,j); //debug(j),debug(i)<<endl; if(l<=r){ ++pre[l],--pre[r+1]; //cout<<l<<" "<<r<<endl; l+=len*2-1,r+=len*2-1; ++nex[l],--nex[r+1]; //cout<<l<<" "<<r<<endl; } } } REP(i,1,n){ pre[i]+=pre[i-1]; nex[i]+=nex[i-1]; } /*REP(i,1,n)printf("%lld ",pre[i]); printf("\n"); REP(i,1,n)printf("%lld ",nex[i]); printf("\n");*/ ll ans=0; REP(i,1,n-1)ans+=nex[i]*pre[i+1]; printf("%lld\n",ans); } int main(){ // File(); REP(i,2,3e4)Log[i]=Log[i>>1]+1; read(T); while(T--){ scanf("%s",S[0].s+1); strcpy(S[1].s+1,S[0].s+1); n=strlen(S[0].s+1); reverse(S[1].s+1,S[1].s+n+1); S[0].suffix_sort(); S[1].suffix_sort(); work(); } return 0; }
[bzoj4650][Noi2016]優秀的拆分——後綴數組