bzoj 2251: [2010Beijing Wc]外星联络 (后缀数组)

2251: [2010Beijing Wc]外星联络

Time Limit: 30 Sec   Memory Limit: 256 MB
Submit: 769   Solved: 459
[ Submit][ Status][ Discuss]

Description

小 P 在看过电影《超时空接触》(Contact)之后被深深的打动,决心致力于寻
找外星人的事业。于是,他每天晚上都爬在屋顶上试图用自己的收音机收听外星
人发来的信息。虽然他收听到的仅仅是一些噪声,但是他还是按照这些噪声的高
低电平将接收到的信号改写为由 0 和 1 构成的串, 并坚信外星人的信息就隐藏在
其中。他认为,外星人发来的信息一定会在他接受到的 01 串中重复出现,所以
他希望找到他接受到的 01 串中所有重复出现次数大于 1 的子串。但是他收到的
信号串实在是太长了,于是,他希望你能编一个程序来帮助他。

Input

输入文件的第一行是一个整数N ,代表小 P 接收到的信号串的长度。 
输入文件第二行包含一个长度为N 的 01 串,代表小 P 接收到的信号串。

Output

输出文件的每一行包含一个出现次数大于1 的子串所出现的次数。输出的顺
序按对应的子串的字典序排列。

Sample Input

7
1010101

Sample Output

3
3
2
2
4
3
3
2
2

HINT

  对于 100%的数据,满足 0 <=  N     <=3000 

Source

[ Submit][ Status][ Discuss]

题解:后缀数组

求出后缀数组后,枚举子串的长度x,然后将height分组,保证每组中的后缀之间的height>=x,然后组中的个数就是此时的出现次数。

注意按照字典序输出,所以我们要把出现次数赋值给每组的第一个后缀,这样按照sa的顺序找答案,就是按照字典序的。

#include
#include
#include
#include
#define N 100003
using namespace std;
int n,m,len,p;
int sa[N],rank[N],height[N],a[N],b[N],xx[N],yy[N],*x,*y,pos[3003][3003];
char s[N];
int cmp(int i,int j,int l)
{
	return y[i]==y[j]&&(i+l>len?-1:y[i+l])==(j+l>len?-1:y[j+l]);
}
void get_SA()
{
	x=xx; y=yy; m=2;
	for (int i=1;i<=len;i++) b[x[i]=a[i]]++;
	for (int i=1;i<=m;i++) b[i]+=b[i-1];
	for (int i=len;i>=1;i--) sa[b[x[i]]--]=i;
	for (int k=1;k<=len;k<<=1) {
		p=0;
		for (int i=len-k+1;i<=len;i++) y[++p]=i;
		for (int i=1;i<=len;i++)
		 if (sa[i]>k) y[++p]=sa[i]-k;
		for (int i=1;i<=m;i++) b[i]=0;
		for (int i=1;i<=len;i++) b[x[y[i]]]++;
		for (int i=1;i<=m;i++) b[i]+=b[i-1];
		for (int i=len;i>=1;i--) sa[b[x[y[i]]]--]=y[i];
		swap(x,y); p=2; x[sa[1]]=1;
		for (int i=2;i<=len;i++)
		 x[sa[i]]=cmp(sa[i],sa[i-1],k)?p-1:p++;
		if (p>len) break;
		m=p+1;
	}
	p=0;
	for (int i=1;i<=len;i++) rank[sa[i]]=i;
	for (int i=1;i<=len;i++){
		if (rank[i]==1) continue;
		int j=sa[rank[i]-1];
		while (i+p<=len&&j+p<=len&&a[i+p]==a[j+p]) p++;
		height[rank[i]]=p;
		p=max(p-1,0);
	}
}
void solve(int x)
{
	int size=1; int last=1;
	for (int i=2;i<=len;i++)
	 if (height[i]>=x) {
	 	size++;
	 }
	 else {
	 	if (size>1) 
	 		pos[sa[last]][x]=size;
		size=1; last=i;
	 }
	if (size>1) 
	  pos[sa[last]][x]=size;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("my.out","w",stdout);
	scanf("%d",&len);
	scanf("%s",s+1);
	for (int i=1;i<=len;i++) a[i]=s[i]-'0';
	get_SA();
	for (int i=1;i<=len;i++)
	 solve(i);
	for (int i=1;i<=len;i++) {
		int t=sa[i]; 
		for (int j=1;j<=len;j++)
		 if (pos[t][j]) printf("%d\n",pos[t][j]);
	}
}



你可能感兴趣的:(后缀数组)