2251: [2010Beijing Wc]外星联络
Time Limit: 30 Sec Memory Limit: 256 MBSubmit: 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
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
Source
题解:后缀数组
求出后缀数组后,枚举子串的长度x,然后将height分组,保证每组中的后缀之间的height>=x,然后组中的个数就是此时的出现次数。
注意按照字典序输出,所以我们要把出现次数赋值给每组的第一个后缀,这样按照sa的顺序找答案,就是按照字典序的。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#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]);
}
}