小 P 在看过电影《超时空接触》(Contact)之后被深深的打动,决心致力于寻 找外星人的事业。于是,他每天晚上都爬在屋顶上试图用自己的收音机收听外星 人发来的信息。虽然他收听到的仅仅是一些噪声,但是他还是按照这些噪声的高 低电平将接收到的信号改写为由 0 和 1 构成的串, 并坚信外星人的信息就隐藏在 其中。他认为,外星人发来的信息一定会在他接受到的 01 串中重复出现,所以 他希望找到他接受到的 01 串中所有重复出现次数大于 1 的子串。但是他收到的 信号串实在是太长了,于是,他希望你能编一个程序来帮助他。
输入文件的第一行是一个整数N ,代表小 P 接收到的信号串的长度。 输入文件第二行包含一个长度为N 的 01 串,代表小 P 接收到的信号串。
输出文件的每一行包含一个出现次数大于1 的子串所出现的次数。输出的顺 序按对应的子串的字典序排列。
7 1010101
3 3 2 2 4 3 3 2 2
对于 100%的数据,满足 0 <= N <=3000
做这道题之前我们需要首先明白一件事情
所有后缀的前缀是字符串的子串
这样我们就把子串的出现资次数转换成了求后缀的前缀的出现次数的问题
在后缀的前缀上搞事情,这会让你想到什么?
没错!后缀数组的Height数组
我们可以在Height数组里面枚举
字典序的话好处理,Height数组就是按字典序排的
首先枚举排名,在Height数组中不断枚举前缀,对于每一个前缀,不断往后枚举Height,枚举的时候统计次数。
哎呀说的好乱,自己看代码把
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=2*1e6+10;
int sa[MAXN],rak[MAXN],tp[MAXN],tax[MAXN],a[MAXN],N,M,height[MAXN];
char s[MAXN];
void Qsort()
{
for(int i=1;i<=M;i++) tax[i]=0;
for(int i=1;i<=N;i++) tax[rak[i]]++;
for(int i=1;i<=M;i++) tax[i]+=tax[i-1];
for(int i=N;i>=1;i--) sa[ tax[rak[tp[i]]]-- ] = tp[i];
}
void Ssort()
{
M=127;
for(int i=1;i<=N;i++) rak[i]=a[i],tp[i]=i;Qsort();
for(int w=1,p=1; p<N ; w<<=1,M=p)
{
p=0;
for(int i=N-w+1;i<=N;i++) tp[++p]=i;
for(int i=1;i<=N;i++) if(sa[i]>w) tp[++p]=sa[i]-w;
Qsort();
swap(tp,rak);
rak[sa[1]]=1;p=1;
for(int i=2;i<=N;i++) rak[sa[i]] = (tp[sa[i]]==tp[sa[i-1]]&&tp[sa[i]+w]==tp[sa[i-1]+w])?p:++p;
}
int j,k=0;
for(int i=1;i<=N;height[rak[i++]]=k)
for(k=k?k-1:k,j=sa[rak[i]-1];a[i+k]==a[j+k];++k );
for(int i=0;i<=N;i++)
{
for(int j=height[i]+1;;j++)
{
int tot=1;
for(int k=i+1;height[k]>=j;++k,++tot);
if(tot>1) printf("%d\n",tot);
else break;
}
}
}
int main()
{
#ifdef WIN32
freopen("a.in","r",stdin);
#else
#endif
int Meiyong;
cin>>Meiyong;
scanf("%s",s);
N=strlen(s);
for(int i=1;i<=N;i++) a[i]=s[i-1];
Ssort();
return 0;
}