不同子串个数
I.不同子串个数
后缀数组在处理子串问题时往往有奇效,因为后缀的前缀即是子串,而后缀数组正是按照前缀排序的后缀。
回到本题。因为后缀的前缀是子串,则一条后缀与其它所有后缀的LCP的最长长度,即是这条后缀的前缀子串中所有被重复计数的串的数量。
我们掏出求得的\(ht\)数组。初学SA时大家一定接触过一个重要的\(\text{LCP Lemma}\),即\(\operatorname{LCP}(i,j)=\min\limits_{i\leq j\leq k}ht_j\)。我们考虑当前后缀与其它任何一条串的LCP,发现它的表达式都包含\(ht_i\),即\(ht_i\)即为LCP的最长长度。则只需要求出所有子串数量减去\(\sum\limits_{i=1}^{n}ht_i\)即可。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll res;
int n,m,sa[1001000],rk[1001000],buc[1001000],x[1001000],y[1001000],ht[1001000];
char s[1001000];
void SA(){
for(int i=1;i<=n;i++)buc[x[i]=s[i]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n;i;i--)sa[buc[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){
int num=0;
for(int i=n-k+1;i<=n;i++)y[++num]=i;
for(int i=1;i<=n;i++)if(sa[i]>k)y[++num]=sa[i]-k;
for(int i=0;i<=m;i++)buc[i]=0;
for(int i=1;i<=n;i++)buc[x[y[i]]]++;
for(int i=1;i<=m;i++)buc[i]+=buc[i-1];
for(int i=n;i;i--)sa[buc[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);
x[sa[1]]=1;
num=1;
for(int i=2;i<=n;i++)x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
if(num==n)break;
m=num;
}
}
void HT(){
for(int i=1;i<=n;i++)rk[sa[i]]=i;
int k=0;
for(int i=1;i<=n;i++){
if(rk[i]==1)continue;
if(k)k--;
int j=sa[rk[i]-1];
while(j+k<=n&&i+k<=n&&s[j+k]==s[i+k])k++;
ht[rk[i]]=k;
}
}
int main(){
scanf("%d%s",&n,s+1),m='z';
SA(),HT();
// for(int i=0;i<n;i++)printf("%d ",sa[i]);puts("");
for(int i=1;i<=n;i++)res+=(n-sa[i]+1)-ht[i];
printf("%lld\n",res);
return 0;
}