【BZOJ2251】[2010Beijing Wc]外星联络 后缀数组

【BZOJ2251】[2010Beijing Wc]外星联络 

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 

题解:先求出sa和height,然后只要沿着sa枚举每一个子串,在height里找出height不小于当前子串长度的范围。为了防止重复,每次枚举从height+1开始。

 

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=3010;
int ra[maxn],rb[maxn],st[maxn],sa[maxn],v[maxn],rank[maxn],h[maxn];
char str[maxn];
int n,m;
void work()
{
    int i,j,k,p,*x=ra,*y=rb;
    for(i=0;i<n;i++)    st[x[i]=v[i]]++;
    for(i=1;i<m;i++)    st[i]+=st[i-1];
    for(i=n-1;i>=0;i--)    sa[--st[x[i]]]=i;
    for(j=1,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)    y[p++]=sa[i]-j;
        for(i=0;i<m;i++)    st[i]=0;
        for(i=0;i<n;i++)    st[x[y[i]]]++;
        for(i=1;i<m;i++)    st[i]+=st[i-1];
        for(i=n-1;i>=0;i--)    sa[--st[x[y[i]]]]=y[i];
        for(swap(x,y),p=1,x[sa[0]]=0,i=1;i<n;i++)
            x[sa[i]]=(y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+j]==y[sa[i]+j])?p-1:p++;
    }
    for(i=0;i<n;i++)    rank[sa[i]]=i;
    for(i=k=0;i<n-1;h[rank[i++]]=k)
        for(k?k--:0,j=sa[rank[i]-1];v[i+k]==v[j+k];k++);
}
int main()
{
    scanf("%d%s",&n,str);
    int i,j,r;
    for(i=0;i<n;i++)    v[i]=str[i]-'0'+1;
    m=3;
    n++;
    work();
    for(i=1;i<n;i++)
    {
        for(j=h[i]+1;sa[i]+j<n;j++)
        {
            //网上的题解这里还向下求了一下区间的左端点l,感觉不明觉厉 
            for(r=i+1;r<=n&&h[r]>=j;r++);
            if(r-i>=2)    printf("%d\n",r-i);    
        }
    }
    return 0;
}
posted @ 2017-01-10 11:26 CQzhangyu 阅读(...) 评论(...) 编辑 收藏