题意:
给出长度为n的串,求这个串中不通的子串个数。
题解:
首先我们分析,这种题应该从每个子串入手,每个子串肯定是后缀的前缀,所以分析后缀的一些关系。单独分析一个后缀能为不通子串个数贡献多少,如果按照SA的顺序不断计算出后缀,当要加入k这个后缀时,他的长度为n-SA[k],那么他可以贡献出的前缀也就是子串的个数肯定有n-SA[k],但是他和前一个后缀有重叠部分重叠部分的长度便是多计算的子串个数,于是减去height[k]。然后把所有后缀的n-SA[k]-height[k]加起来就是答案。
#include<iostream> #include<math.h> #include<stdio.h> #include<algorithm> #include<string.h> #include<vector> #include<queue> #include<map> #include<set> using namespace std; #define B(x) (1<<(x)) typedef long long ll; const int oo=0x3f3f3f3f; const ll OO=1LL<<61; const int MOD=10007; const int maxn=1005; int rank[maxn],SA[maxn],height[maxn]; int t1[maxn],t2[maxn],t3[maxn],cnt[maxn]; char str[maxn]; void Swap(int*& x,int*& y){ int *temp=x; x=y; y=temp; } int lcp(int t[],int a,int b,int l){ return t[a]==t[b]&&t[a+l]==t[b+l]; } void build_SA(char s[],int len,int up){ int *k1=t1,*k2=t2,*r=t3; for(int i=0;i<up;i++)cnt[i]=0; for(int i=0;i<len;i++)cnt[k1[i]=s[i]]++; for(int i=1;i<up;i++)cnt[i]+=cnt[i-1]; for(int i=len-1;i>=0;i--)SA[--cnt[k1[i]]]=i; for(int d=1,p=0;p<len;d<<=1){ for(int i=len-d;i<len;i++)k2[p++]=i; for(int i=0;i<len;i++)if(SA[i]>=d)k2[p++]=SA[i]-d; for(int i=0;i<len;i++)r[i]=k1[k2[i]]; for(int i=0;i<up;i++)cnt[i]=0; for(int i=0;i<len;i++)cnt[r[i]]++; for(int i=1;i<up;i++)cnt[i]+=cnt[i-1]; for(int i=len-1;i>=0;i--)SA[--cnt[r[i]]]=k2[i]; Swap(k1,k2); k1[SA[0]]=0; p=1; for(int i=1;i<len;i++){ k1[SA[i]]= lcp(k2,SA[i-1],SA[i],d) ? p-1 : p++; } if(p>=len)return; up=p; p=0; } } void get_height(char s[],int len) { for(int i=1;i<=len;i++)rank[SA[i]]=i; for(int i=0,p=0;i<len;i++){ int j=SA[rank[i]-1]; while(s[i+p]==s[j+p])p++; height[rank[i]]=p; if(p)p--; } } int main(){ int T; scanf("%d",&T); while(T--){ scanf("%s",str); int len=strlen(str); str[len]='#'; build_SA(str,len+1,128); get_height(str,len); ll ans=0; for(int i=1;i<=len;i++){ ans+=len-SA[i]-height[i]; } printf("%I64d\n",ans); } return 0; } /** */