• 【BZOJ2251】外星联络


    2251: [2010Beijing Wc]外星联络

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

    /*In Search Of Life*/
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<iomanip>
    #include<stack>
    #include<map>
    #include<set>
    #include<cmath>
    #define debug(x) cerr<<#x<<"="<<x<<endl
    #define INF 0x7f7f7f7f
    #define llINF 0x7fffffffffffll
    using namespace std;
    typedef pair<int,int> pii;
    typedef long long ll;
    inline int init()
    {
        int now=0,ju=1;char c;bool flag=false;
        while(1)
        {
            c=getchar();
            if(c=='-')ju=-1;
            else if(c>='0'&&c<='9')
            {
                now=now*10+c-'0';
                flag=true;
            }
            else if(flag)return now*ju;
        }
    }
    inline long long llinit()
    {
        long long now=0,ju=1;char c;bool flag=false;
        while(1)
        {
            c=getchar();
            if(c=='-')ju=-1;
            else if(c>='0'&&c<='9')
            {
                now=now*10+c-'0';
                flag=true;
            }
            else if(flag)return now*ju;
        }
    }
    int n,m,auxa[10005],auxsort[10005],auxb[10005],auxval[10005],sa[10005],height[10005],rank[10005];
    char str[10005];
    int ans[5000505];
    int c[10005];
    int cnt=0;
    void getsa()
    {
        int *x=auxa,*y=auxb,t,cnt=0;m=256;
        for(int i=1;i<=n;i++)auxsort[x[i]=str[i]]++;
        for(int i=2;i<=m;i++)auxsort[i]+=auxsort[i-1];
        for(int i=n;i>=1;i--)sa[auxsort[x[i]]--]=i;
        for(int j=1;cnt<n;j<<=1,m=cnt)
        {
            cnt=0;
            for(int i=n-j+1;i<=n;i++)y[++cnt]=i;
            for(int i=1;i<=n;i++)if(sa[i]-j>0)y[++cnt]=sa[i]-j;
            for(int i=1;i<=n;i++)auxval[i]=x[y[i]];
            for(int i=0;i<=m;i++)auxsort[i]=0;
            for(int i=1;i<=n;i++)++auxsort[auxval[i]];
            for(int i=2;i<=m;i++)auxsort[i]+=auxsort[i-1];
            for(int i=n;i>=1;i--)sa[auxsort[auxval[i]]--]=y[i];
            swap(x,y);cnt=x[sa[1]]=1;
            for(int i=2;i<=n;i++)
            {
                if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+j]==y[sa[i-1]+j])x[sa[i]]=cnt;
                else x[sa[i]]=++cnt;
            }
        }
        for(int i=1;i<=n;i++)rank[sa[i]]=i;
        cnt=0;
        for(int i=1;i<=n;i++)
        {
            if(rank[i]==1)continue;
            if(cnt)cnt--;
            int j=sa[rank[i]-1];
            while(str[i+cnt]==str[j+cnt])++cnt;
            height[rank[i]]=cnt;
        }
        return;
    }
    int main()
    {
        n=init();scanf("%s",str+1);
        getsa();
        for(int i=n;i>=1;i--)
        {
            for(int j=1;j<=height[i+1];j++)
            {
                c[j]++;
            }
            for(int j=n;j>=height[i+1]+1;j--)
            {
                if(c[j]>1)ans[++cnt]=c[j];
                c[j]=1;
            }
        }
        for(int j=n;j>=1;j--)
        {
            if(c[j]>1)ans[++cnt]=c[j];
        }   
        for(int i=cnt;i>=1;i--)printf("%d
    ",ans[i]);
        return 0;
    }
    View Code

    感谢涵哥指导

    跑了160ms 我觉得还是挺快的

    涵哥@goodqt 说这题最快可以$O(min(字符集大小log字符集大小,n^{2})+n^{2})$

    具体做法是这样

    我们考虑从后往前加入每个字符串

    比如说 

    5

    10101

    这样求出来rank是

    01 0

    0101 2

    1 0

    101 1

    10101 3

    然后我们从后往前扫,记录数组ans[]

    每次我们将ans从1到n置为1

    枚举i从n到1 将1-height[i+1]++ height[i+1]-n扫一下看看有没有超过1的数 有就全部弹出到一个栈内

    然后这样最后全部弹掉 然后我们从上往下把所有元素输出即可

    证明:这样一定满足字典序且不重不漏

    每个子串只会在它应该被统计的时候统计上 且我们从后往前枚举 一定保证了字典序大的数最先被压进去。

    证明:这样一定能统计出答案

    每个重复子串只会在lcp处被计算 那么显然最后所有子串都会被统计,得证。

     UPD:

    我发现这是我最近做的SA题 写点东西提醒一下自己

    假如遇到bzoj1717这种题 可以考虑全程用getVal()比较 而不是直接比较是否相等(会出现全等于0的情况)

    以及最大的问题 不要别人说啥就是啥 自己试一试!

  • 相关阅读:
    [Erlang33]使用recon从网页查看Erlang运行状态
    Oracle数据库备份与恢复
    Oracle 差异增量和累计增量备份
    RMAN 参数详解
    前端自动化构建工具 Webpack—— 2 webpack最基本的使用方式
    React 入门与实战-课时7 虚拟DOM的本质和目的
    数字证书注册审批机构(RA)
    前端实习面试整理
    JavaScript 对象中this的指向问题
    CSS基础学习 21.CSS居中总结
  • 原文地址:https://www.cnblogs.com/redwind/p/6655057.html
Copyright © 2020-2023  润新知