不同子串个数

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;
}

上一篇:智慧城市大数据运营中心 IOC 之 Web GIS 地图应用


下一篇:【大厂面试题系列】:说说Redis的rehash过程