[后缀数组、不重复子串]SPOJ694、spoj705--Distinct Substrings

题目大意:给出一个字符串,问它的不重复子串有多少个。两题是一样的,除了字符串长度.......

思路:用后缀数组可以轻松解决。因为这个字符串的每个子串必然是某个后缀的前缀,先用后缀数组求出sa和height,那么对于sa[k],它有n-sa[k]个子串,其中有height[k]个是和上一个后缀重复的,所以要减去。所以用后缀数组求解的时间复杂度是O(n),后缀数组要是用倍增算法是O(nlog2n),效率很高。

代码:

#include<iostream>

#include<cstring>

#include<cstdio>

using namespace std;

#define maxn 50010

int wa[maxn],wb[maxn],wv[maxn];

int wsf[maxn];

int cmp(int *r,int a,int b,int k)

{

	return r[a]==r[b]&&r[a+k]==r[b+k];

}

void getsa(int *r,int *sa,int n,int m)

{

	int i,j,p,*x=wa,*y=wb,*t;

	for(i=0;i<m;i++)

	wsf[i]=0;

	for(i=0;i<n;i++)

	wsf[x[i]=r[i]]++;

	for(i=1;i<m;i++)

	wsf[i]+=wsf[i-1];

	for(i=n-1;i>=0;i--)

	sa[--wsf[x[i]]]=i;

	for(j=1,p=1;p<n;j*=2,m=p)

	{

		for(p=0,i=n-j;i<n;i++)  y[p++]=i;

		for(i=0;i<n;i++)  if(sa[i]>=j)  y[p++]=sa[i]-j;

		for(i=0;i<n;i++)  wv[i]=x[y[i]];

		for(i=0;i<m;i++)  wsf[i]=0;

		for(i=0;i<n;i++)  wsf[wv[i]]++;

		for(i=1;i<m;i++)  wsf[i]+=wsf[i-1];

		for(i=n-1;i>=0;i--)  sa[--wsf[wv[i]]]=y[i];

		t=x;

		x=y;

		y=t;

		x[sa[0]]=0;

		for(p=1,i=1;i<n;i++)  x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;

	}

	return;

}

int rank[maxn],h[maxn];

void geth(int *r,int *sa,int n)

{

	int i,j,k=0;

	for(i=1;i<=n;i++)

	rank[sa[i]]=i;

	for(i=0;i<n;i++)

	{

		if(k)

		k--;

		else

		k=0;

		j=sa[rank[i]-1];

		while(r[i+k]==r[j+k])

		k++;

		h[rank[i]]=k;

	}

}

int main()

{

	int text;

	scanf("%d",&text);

	while(text--)

	{

		char s[maxn];

		int t[maxn];

		int sa[maxn];

		scanf("%s",s);

		int len=strlen(s);

		for(int i=0;i<len;i++)

		t[i]=s[i];

		t[len]=0;

		int v='z';

		getsa(t,sa,len+1,v+1);

		geth(t,sa,len);

		int sum=0;

		for(int i=1;i<=len;i++)

		sum+=len-sa[i]-h[i];

		printf("%d\n",sum);

	}

	return 0;

}

 

 

 

你可能感兴趣的:(substring)