SPOJ-SUBST1 New Distinct Substrings(后缀数组)
题目大意:判断总共有多少种不同的子串。
题目分析:不同的子串数目为 Σ(后缀SA[i]的长度-height[i])。
代码如下:
# include<iostream> # include<cstdio> # include<cstring> # include<algorithm> using namespace std; # define LL long long const int N=50000; char str[N+5]; int n,SA[N+5],cnt[N+5]; int rk[N+5],tSA[N+5]; LL ans; bool same(int i,int j,int k) { if(tSA[i]!=tSA[j]) return false; if(i+k>=n&&j+k>=n) return true; if(i+k>=n&&j+k<n) return false; if(i+k<n&&j+k>=n) return false; return tSA[i+k]==tSA[j+k]; } void buildSA() { int m=130; for(int i=0;i<m;++i) cnt[i]=0; for(int i=0;i<n;++i) ++cnt[rk[i]=(int)str[i]]; for(int i=1;i<m;++i) cnt[i]+=cnt[i-1]; for(int i=n-1;i>=0;--i) SA[--cnt[rk[i]]]=i; for(int k=1;k<=n;k<<=1){ int p=0; for(int i=n-k;i<n;++i) tSA[p++]=i; for(int i=0;i<n;++i) if(SA[i]>=k) tSA[p++]=SA[i]-k; for(int i=0;i<m;++i) cnt[i]=0; for(int i=0;i<n;++i) ++cnt[rk[tSA[i]]]; for(int i=1;i<m;++i) cnt[i]+=cnt[i-1]; for(int i=n-1;i>=0;--i) SA[--cnt[rk[tSA[i]]]]=tSA[i]; swap(rk,tSA); p=1; rk[SA[0]]=0; for(int i=1;i<n;++i) rk[SA[i]]=same(SA[i],SA[i-1],k)?p-1:p++; if(p>=n) break; m=p; } } void getHeight() { for(int i=0;i<n;++i) rk[SA[i]]=i; ans=0; int k=0; for(int i=0;i<n;++i){ if(rk[i]==0) k=0; else{ if(k) --k; int j=SA[rk[i]-1]; while(i+k<n&&j+k<n&&str[i+k]==str[j+k]) ++k; } ans+=n-SA[i]-k; } } int main() { int T; scanf("%d",&T); while(T--) { scanf("%s",str); n=strlen(str); buildSA(); getHeight(); printf("%lld\n",ans); } return 0; }