• 模板——后缀数组


    后缀数组的详解参见此博客:https://www.cnblogs.com/victorique/p/8480093.html   这里主要理一下思路和注意点

    后缀数组基本介绍:

    后缀数组就是对一个字符串的$n$个后缀进行排序,但是考虑到每一个字符串都有一个长度,一位位比下来肯定炸飞,所以要优化。

    先对于每一个字符进行离散化,每次按照倍增和前面的字符合并,然后合并后进行双关键字排序,再离散化,直至没有完全相同的值为止

    总复杂度为$O(nlog(n))$

    代码思路:第一次先预处理离散化,再塞进桶里。后面每次都是二维桶排序,再离散化合并,更新桶,每次用一个pair数组维护双关键字排序的数组信息

    (之前没用pair,全用离散化数组num,结果第二关键字就被吃了。。)

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int N=1020000;
    
    int sa[N],rk[N],n;
    char s[N];
    
    pair<int,int> t[N];
    int totb=0,tota=0;
    int a[N],b[N],num[N];//now:桶的个数 
    int pre[N],ord[N],cnt[N];//排名为i的id 
    bool base_sort()
    {
        int bl=1;
        for(int i=2;i<=n;i++) if(num[ord[i]]==num[ord[i-1]]) bl=0;
        if(bl) return 1;
        
        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<=totb;i++) cnt[i]=b[i];
        for(int i=1;i<=totb;i++) cnt[i]+=cnt[i-1];
        for(int i=1;i<=n;i++) pre[cnt[t[i].second]--]=i;
        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<=tota;i++) cnt[i]=a[i];
        for(int i=1;i<=tota;i++) cnt[i]+=cnt[i-1];//按第一关键字开桶 
        for(int i=n;i>=1;i--) ord[cnt[t[pre[i]].first]--]=pre[i];//按第二关键字从大到小进桶
        
        int now=0; tota=0,totb=0;
        for(int i=1;i<=n;i++)
        {
            if(t[ord[i]]!=t[ord[i-1]]) now++;
            num[ord[i]]=now;
        }
        return 0;
    }
    
    void merge(int B)
    {
        //cout<<B<<endl;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        for(int i=1;i<=n;i++)
        {
            tota=max(tota,num[i]);
            totb=max(totb,num[i+(1<<B)]);
            t[i]=make_pair(num[i],num[i+(1<<B)]);
            a[num[i]]++; 
            b[num[i+(1<<B)]]++;
        }
    }
    
    pair<int,int> p[N];
    void init()
    {
        for(int i=1;i<=n;i++) 
        {
            p[i]=make_pair(s[i]-'A'+1,i);
        }
        sort(p+1,p+n+1);
        int now=0;
        for(int i=1;i<=n;i++) 
        {
            if(p[i].first!=p[i-1].first) now++;
            num[p[i].second]=now;
        }
        for(int i=1;i<=n;i++)
        {
            tota=max(tota,num[i]);
            t[i]=make_pair(num[i],0);
            a[num[i]]++; 
            b[0]++;
        }
    }
    
    int main() 
    {
        scanf("%s",s+1);
        n=strlen(s+1);
        init();
        int B=0;
        while(!base_sort()) merge(B++);
        for(int i=1;i<=n;i++) printf("%d ",ord[i]);
        return 0;
    }

    一般不会只靠一个后缀数组,所以再增加一个辅助概念:LCP

    (h[i]>=h[i-1]-1:令i位置所找到的height为str1,i-1位置所找到的height为str2,则若str2长度大于str1长度加1,则将str2的第一位挖掉,必定优于i所找到的height,矛盾)

    后缀数组的具体应用:

  • 相关阅读:
    深度学习三巨头Hinton,Bengio,LeCunn共摘本年度ACM图灵奖(ACM A.M. Turing Award)
    【我为之而活的三种激情】by 伯特兰·罗素
    遥感高光谱分类文献阅读:Going Deeper with Contextual CNN for Hyperspectral Image Classification
    遥感高光谱分类文献阅读:Exploring Hierarchical Convolutional Features for Hyperspectral Image Classification
    大乘百法明门论笔记
    太宰治【人间失格】
    论文笔记:Accurate Causal Inference on Discrete Data
    因果推断(Causal Inference)概要
    阿毗达摩基本概念
    我们必须知道,我们终将知道。
  • 原文地址:https://www.cnblogs.com/Forever-666/p/11746105.html
Copyright © 2020-2023  润新知