BZOJ_2251_[2010Beijing Wc]外星联络_后缀数组
BZOJ_2251_[2010Beijing Wc]外星联络_后缀数组
Description
小 P 在看过电影《超时空接触》(Contact)之后被深深的打动,决心致力于寻
找外星人的事业。于是,他每天晚上都爬在屋顶上试图用自己的收音机收听外星
人发来的信息。虽然他收听到的仅仅是一些噪声,但是他还是按照这些噪声的高
低电平将接收到的信号改写为由 0 和 1 构成的串, 并坚信外星人的信息就隐藏在
其中。他认为,外星人发来的信息一定会在他接受到的 01 串中重复出现,所以
他希望找到他接受到的 01 串中所有重复出现次数大于 1 的子串。但是他收到的
信号串实在是太长了,于是,他希望你能编一个程序来帮助他。
Input
输入文件的第一行是一个整数N ,代表小 P 接收到的信号串的长度。
输入文件第二行包含一个长度为N 的 01 串,代表小 P 接收到的信号串。
Output
输出文件的每一行包含一个出现次数大于1 的子串所出现的次数。输出的顺
序按对应的子串的字典序排列。
Sample Input
7
1010101
1010101
Sample Output
3
3
2
2
4
3
3
2
2
3
2
2
4
3
3
2
2
HINT
对于 100%的数据,满足 0 <= N <=3000
由于是可重复子串所以有一些好做。
对整个串求出sa和height数组。
然后从前往后枚举子串,向后扩展,直到某个height[i]小于枚举子串的长度。
需要注意枚举的子串长度应该从height[i]+1开始枚举,因为比它短的那些子串在前面已经枚举过了。
时间复杂度O(nlogn+答案)<=O(n^2)。
代码:
#include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define N 3050 int n,ws[N],wv[N],wa[N],wb[N],sa[N],m=3,r[N]; int rank[N],height[N]; char str[N]; void build_suffix_array() { int i,j,p,*x=wa,*y=wb,*t; for(i=0;i<m;i++) ws[i]=0; for(i=0;i<n;i++) ws[x[i]=r[i]]++; for(i=1;i<m;i++) ws[i]+=ws[i-1]; for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i; for(j=p=1;p<n;j<<=1,m=p) { for(p=0,i=n-j;i<n;i++) y[p++]=i; for(i=0;i<n;i++) if(sa[i]-j>=0) y[p++]=sa[i]-j; for(i=0;i<n;i++) wv[i]=x[y[i]]; for(i=0;i<m;i++) ws[i]=0; for(i=0;i<n;i++) ws[wv[i]]++; for(i=1;i<m;i++) ws[i]+=ws[i-1]; for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i]; for(t=x,x=y,y=t,x[sa[0]]=0,p=i=1;i<n;i++) { if(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j]) x[sa[i]]=p-1; else x[sa[i]]=p++; } } for(i=1;i<n;i++) rank[sa[i]]=i; for(i=p=0;i<n-1;height[rank[i++]]=p) for(p?p--:0,j=sa[rank[i]-1];r[i+p]==r[j+p];p++); } int main() { scanf("%d",&n); scanf("%s",str); int i,j,k; for(i=0;i<n;i++) r[i]=str[i]-'0'+1; n++; build_suffix_array(); for(i=1;i<=n;i++) { for(j=height[i]+1;sa[i]+j-1<n;j++) { for(k=i+1;height[k]>=j&&k<=n;k++) ; if(k-i>1) printf("%d\n",k-i); } } }