zoukankan      html  css  js  c++  java
  • 洛谷P3809 【模板】后缀排序 后缀数组

    网址:https://www.luogu.org/problem/P3809

    题意:

    把一个字符串的所有后缀按照字典序排序。字符串长度小于$1e6$。

    题解:

    裸的后缀数组,原理就是先把起始位置是$i$后缀作为第一关键字,$i+1$的作为第二关键字,然后合并关键字,得到新的第一关键字,然后$i+2$作为第二关键字,再合并,然后依次倍增。具体实现过程见代码注释:

    参考博客:

    https://www.cnblogs.com/victorique/p/8480093.html

    https://www.luogu.org/blog/black-jokers/solution1-p3809

    https://www.cnblogs.com/ezoiLZH/p/9607849.html

    AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN=1000005;
    struct SuffixArray
    {
        int n,m;
        int tax[MAXN],rank[MAXN],tp[MAXN],sa[MAXN];
        //sa[i]=j,表示第i名的后缀是从j开始的(注意存的是下标)
        //rank[i]=j,从i开始的后缀是第j名的(和sa互逆,是排名(值))
        //tp[i],第二关键字为 i 的后缀是从j开始的 (相当于是第二关键字的sa数组,存的是下标)
        //tax[i]=j,表示第一关键字为i的数有j个(基数排序时用的桶,是值)
        char ch[MAXN];
        void sort()//基数排序
        {
            for(int i=0;i<=m;++i)//桶清零
                tax[i]=0;
            for(int i=1;i<=n;++i)//每个数放到对应的桶,得到的是从i开始的后缀的第一关键字是第rank[i]名,
            //这样的名次的后缀有tax[rank[i]]个
                ++tax[rank[i]];
            for(int i=1;i<=m;++i)//加上前面的桶,排序完成,得到排名
                tax[i]+=tax[i-1];
            for(int i=n;i>=1;--i)//以i为第二关键字的后缀是后缀tp[i],tax[rank[tp[i]]]=j是以i为第二关键
            //字的后缀的第一关键字为第一关键字的后缀有j个,由于基数排序后求前缀和已经知道其排名,所以就把这
            //个排名对应的那个后缀tp[i]记录下来,同时排名减1。每一次都是取以i为第二关键字的后缀的第一关键字
            //为第一关键字的后缀的最后一名,这样子就相当于是第二关键字相同,按照第一关键字排序了,如果第二关
            //键字不相同,也是第一关键字大的,一定越靠后,此时保证了按照第一第二关键字进行排序。
                sa[tax[rank[tp[i]]]--]=tp[i];
        }
        bool cmp(int a,int b,int k)
        {
            //现在这个tp是上一次rank,
            //前项的意思是从sa[i]开始和从sa[i-1]开始的后缀是的名次是否一样,
            //后项同理,加j之后意味着比较的名次是第二关键字。保证不会越界
            return tp[a]==tp[b]&&tp[a+k]==tp[b+k];
        }
        void get_sa()
        {
            n=strlen(ch+1);
            for(int i=1;i<=n;++i)
                m=max(m,rank[i]=ch[i]-'0'),tp[i]=i;//第一轮第一关键字直接按ASCII码,第二关键字按序号
            sort();//第一轮基数排序
            for(int p=0,j=1;p<n;j<<=1,m=p)
            {
                p=0;//清空
                for(int i=1;i<=j;++i)
                    tp[++p]=n-j+i;//后面的这些后缀i小于等于j的,只有它自己了,所以排在最后面,第二关键字按长度减小而增大
                for(int i=1;i<=n;++i)
                    if(sa[i]>j)
                        tp[++p]=sa[i]-j;//sa[i]>j证明后缀i的长度大于j,tp[sa[i]-j]就是后缀sa[i]-j的第二关键字,此时仍是乱序
                sort();//第二轮及以后的基数排序
                swap(rank,tp);//tp没有用了,我们将rank的信息复制给tp。同时把rank空出来
                rank[sa[1]]=p=1;//空出来的rank记录新的sa下的对应关系,进行下一轮排序
                for(int i=2;i<=n;++i)
                    rank[sa[i]]=cmp(sa[i],sa[i-1],j)?p:++p;
            }
        }
    };
    SuffixArray sa;
    int main()
    {
        //scanf("%d",&sa.n);
        scanf("%s",sa.ch+1);
        sa.get_sa();
        for(int i=1;i<=sa.n;++i)
            printf("%d%c",sa.sa[i],(i==sa.n?'
    ':' '));
        return 0;
    }
    

      

  • 相关阅读:
    LiveNVS实现摄像头RTSP无插件播放,并集中化管理
    liveplayer免费网页直播_点播播放器-页面动态多播放器添加代码示例
    JavaScript之图片滚动
    JavaScript之图片轮换
    DOS命令
    jquery之音乐均衡器
    JavaScript之可运行按钮
    jQuery之点击弹出图标环形菜单
    Android之View方法
    Android之所有权限
  • 原文地址:https://www.cnblogs.com/Aya-Uchida/p/11361472.html
Copyright © 2011-2022 走看看