[bzoj2251][后缀数组][2010Beijing Wc]外星联络

来源:互联网 发布:扒谱子软件 编辑:程序博客网 时间:2024/05/16 15:28

2251: [2010Beijing Wc]外星联络

Time Limit: 30 Sec Memory Limit: 256 MB
Submit: 916 Solved: 556
[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

sol:
发现是求本质不同的子串的出现次数。要求字典序,所以在后缀数组上走。并且只能做前缀,因为前缀我们能保证字典序。
接着考虑一个sa[i]提供的贡献,如果hei[i]>hei[i-1]的话,说明出现了一个新的子串,那这个子串已经出现在了i-1和i(并且不会出现在更前面,这个显然),我们考虑这个子串出现了几次,显然只要往后走一走,看一下hei有没有比这个子串大就行了

#include<cstdio>#include<algorithm>#include<string>#include<cstring>#include<cstdlib>#include<cmath>#include<iostream>using namespace std;int n,m;inline int read(){    char c;    int res,flag=0;    while((c=getchar())>'9'||c<'0') if(c=='-')flag=1;    res=c-'0';    while((c=getchar())>='0'&&c<='9') res=(res<<3)+(res<<1)+c-'0';    return flag?-res:res;}const int N=3100;int sa[N],rank[N],w[N],Max,x[N],hei[N]; char sr[N];inline void Sa(){    int j,u,v,m=127,cnt;    for(int i=1;i<=n;++i) w[x[i]=sr[i]]++;    for(int i=1;i<=m;++i) w[i]+=w[i-1];    for(int i=n;i>=1;--i) sa[w[x[i]]--]=i;    for(j=1;;j<<=1)    {        cnt=0;        for(int i=n-j+1;i<=n;++i) rank[++cnt]=i;        for(int i=1;i<=n;++i)        if(sa[i]>j) rank[++cnt]=sa[i]-j;        for(int i=1;i<=m;++i) w[i]=0;        for(int i=1;i<=n;++i) w[x[i]]++;        for(int i=1;i<=m;++i) w[i]+=w[i-1];        for(int i=n;i>=1;--i) sa[w[x[rank[i]]]--]=rank[i];        m=0;        for(int i=1;i<=n;++i)        {            u=sa[i];v=sa[i-1];            if(x[u]!=x[v]||x[u+j]!=x[v+j]) ++m;            rank[u]=m;        }        if(m==n) break;        for(int i=1;i<=n;++i) x[i]=rank[i];    }    j=0;    for(int i=1;i<=n;++i)    {        v=sa[rank[i]-1];        j=max(j-1,0);        while(sr[i+j]==sr[v+j]) ++j;        hei[rank[i]]=j;    }}int main(){//  freopen("2251.in","r",stdin);//  freopen(".out","w",stdout);    n=read();    scanf("%s",sr+1);    for(int i=1;i<=n;++i) sr[i]++;    Sa();    int pos,high=0,ans;    for(int i=2;i<=n;++i)    {        if(hei[i]>hei[i-1])        for(int j=high+1;j<=hei[i];++j)        {            pos=i;            ans=1;            while(hei[pos]>=j) ans++,pos++;            printf("%d\n",ans);        }        high=hei[i];    } }