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

  

posted @ 2016-10-12 22:25  20143605  阅读(187)  评论(0编辑  收藏  举报