HDU 5769 Substring(後綴數組)
阿新 • • 發佈:2019-03-23
scanf 第一個 turn bit += bits end ans case 的限制後,這樣分析仍然可行。限制增加了出現 \(X\),那預處理出每個位置往後的第一個 \(X\)(本身也可以)的位置 \(nxt\) ,然後把要減去的值改成 \(\max(H[i],nxt[sa[i]]-sa[i])\) 即可。
題意
求字符串 \(S\) 本質不同且一定包含字符 \(X\) 的子串個數。
\(1 \leq |S| \leq 10^5\)
思路
求一個長度為 \(n\) 的串 \(S\) 本質不同的子串個數可以使用公式 \(\displaystyle\sum_{i=1}^{n} n-sa[i]+1-H[i]\) ,其中 \(H[0]=1\)。
這個公式是比較好證的。後綴的前綴就是子串,我們分析每個後綴有多少個對答案有貢獻的前綴,\(n-sa[i]+1\) 表示所有前綴的個數, \(H[i]\) 即 \(\text{lcp}(sa[i],sa[i-1])\) ,表示前面出現過重復的前綴個數,減掉即可。
那有了一個出現 \(X\)
代碼
#include<bits/stdc++.h> #define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i) #define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i) template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;} template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;} typedef long long ll; const int N=1e5+5; int sa[N],rk[N],H[N],tmp[3][N]; char s[N],str[N]; int nxt[N]; char X; int n; void get_SA(int m) { memset(tmp,0,sizeof(tmp)); int *x=tmp[0],*y=tmp[1],*c=tmp[2]; FOR(i,1,n)c[x[i]=s[i]]++; FOR(i,1,m)c[i]+=c[i-1]; DOR(i,n,1)sa[c[x[i]]--]=i; for(int k=1;k<=n;k<<=1) { int p=0; FOR(i,n-k+1,n)y[++p]=i; FOR(i,1,n)if(sa[i]>k)y[++p]=sa[i]-k; FOR(i,0,m)c[i]=0; FOR(i,1,n)c[x[y[i]]]++; FOR(i,1,m)c[i]+=c[i-1]; DOR(i,n,1)sa[c[x[y[i]]]--]=y[i]; std::swap(x,y); p=x[sa[1]]=1; FOR(i,2,n)x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p:++p; if(n==p)break; m=p; } FOR(i,1,n)rk[sa[i]]=i; int k=0; H[1]=0; FOR(i,1,n) { if(k)k--; if(rk[i]==1)continue; int j=sa[rk[i]-1]; while(i+k<=n&&j+k<=n&&s[i+k]==s[j+k])k++; H[rk[i]]=k; } } ll query() { ll ans=0; FOR(i,1,n)ans+=n-sa[i]+1-std::max(H[i],nxt[sa[i]]-sa[i]); return ans; } int main() { int T; scanf("%d",&T); FOR(Ti,1,T) { scanf(" %c%s",&X,s+1); n=strlen(s+1); get_SA(256); FOR(i,1,n) { if(s[i]==X)nxt[i]=i; else nxt[i]=n+1; } DOR(i,n-1,1)chk_min(nxt[i],nxt[i+1]); printf("Case #%d: %lld\n",Ti,query()); } return 0; }
HDU 5769 Substring(後綴數組)