Distinct Substrings(后缀数组)

题意

求一个字符串的不同子串数量

思路

该字符串的所有后缀子串的前缀子串即为改字符串的所有子串数量。总的子串数量=len*(len+1)/2,那么重复的子串如何求呢?即为height数组的总和,每个子串一定是某个后缀的前缀,那么原问题等价于求所有后缀之间的不相同的前缀的个数。

以下摘抄自罗穗骞 《后缀数组——处理字符串的有力工具》论文在github上的网址

如果所有的后缀按照suffix(sa[1]),suffix (sa[2]),suffix(sa[3]),… , suffix (sa[n])的顺序计算,不难发现,对于每一次新加进来的后缀suffix(sa[k]), 它将产生n-sa[k]+1个新的前缀。但是其中有height [k]个是和前面的字符串的前缀是相同的。所以suffix(sa[k])将“贡献”出n-sa[k]+1- height [k]个不同的子串。累加后便是原问题的答案。这个做法的时间复杂度为0(n)


#pragma GCC optimize(2)
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long ul;
typedef unsigned long long ull;
#define pi acos(-1.0)
#define e exp(1.0)
#define pb push_back
#define mk make_pair
#define fir first
#define sec second
#define scf scanf
#define prf printf
typedef pair<ll,ll> pa;
//const ll INF=0x3f3f3f3f3f3f3f3f;
const ll maxn=2e4+7;
ll T,N,K,rank[maxn],sa[maxn],height[maxn],tmp[maxn];
string s;
bool cmp(ll i,ll j){
    
    
	if(rank[i]!=rank[j])
	return rank[i]<rank[j];
	ll r1=i+K<=N?rank[i+K]:-1;
	ll r2=j+K<=N?rank[j+K]:-1;
	return r1<r2;
}
void do_sa(){
    
    
	ll i,j;
	for(i=0;i<=N;i++){
    
    
		sa[i]=i;
		rank[sa[i]]=(i!=N?s[i]:-1);
	}
	for(K=1;K<=N;K<<=1){
    
    
		sort(sa,sa+1+N,cmp);
		tmp[sa[0]]=0;
		for(i=1;i<=N;i++){
    
    
			tmp[sa[i]]=tmp[sa[i-1]]+(cmp(sa[i-1],sa[i])?1:0);
		}
		for(i=0;i<=N;i++)
		rank[i]=tmp[i];
	}
	return ;
}
void get_height(){
    
    
	ll i,j,k=0;
	for(i=0;i<N;i++){
    
    
		if(k)
		k--;
		else
		k=0;
		j=sa[rank[i]-1];
		while(s[i+k]==s[j+k])
		k++;
		height[rank[i]]=k;
	}
	return ;
}
int main()
{
    
    
//  freopen(".../.txt","w",stdout);
//  freopen(".../.txt","r",stdin);
	ios::sync_with_stdio(false);
	cin>>T;
	ll i,j,k;
	while(T--){
    
    
		cin>>s;
		N=s.length();
		do_sa();
		get_height();
		ll res=N*(N+1)/2;
		for(i=1;i<=N;i++)
//		cout<<height[i]<<' ';
		res-=height[i];
		cout<<res<<endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43311695/article/details/107675926