• A B-Suffix Array(后缀数组(新板))


    题:https://ac.nowcoder.com/acm/contest/5666/A

    题意:给定字符串,依题意求出每个后缀串的B数组,然后进行字典序排序

    分析:直接通过1~n串的B数组,我们无法根据当前的B数组来推出其他后缀的B数组,如果可以则直接后缀数组即可;

       通过观察,我们可以设立一个R[i]数组表示B[i]后缀i...n第2个0出现的位置,那么R[i]截取出来的那一段一定是”0X0“(X代表若干个1);

       为什么要这么取呢?因为对于”0X0“的字典序比较,此时的字典树就是改字符串的长度,而对于R[i]后面的部分,通过举例可以发现,R[i]后面部分与B[1]的某一后缀相同,所以对于"0X0”长度相同的部分他们的字典序就等于其在B[1]中的后缀字典序。(这部分对B[1]进行后缀数组的预处理即可)

    #include<bits/stdc++.h>
    using namespace std;
    #define pb push_back
    #define MP make_pair
    typedef long long ll;
    typedef unsigned long long ull;
    const int mod=998244353;
    const int M=1e5+5;
    const int inf=0x3f3f3f3f;
    const ll INF=1e18;
    
    struct SA{
        int sa[M],c[M],Rank[M],t1[M],t2[M];
        int n;
        void gao(int* s,int m){
            s[++n]=0;
            int* x=t1,*y=t2;
            for(int i=0;i<m;i++) c[i]=0;
            for(int i=0;i<n;i++) c[x[i]=s[i]]++;
            for(int i=1;i<m;i++) c[i]+=c[i - 1] ;
            for(int i=n-1; i >= 0; i--) sa[--c[x[i]]]=i;
            for(int k=1;k<=n;k<<=1){
                int p=0 ;
                for(int i=n-k;i<n;i++) y[p++]=i ;
                for(int i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
                for(int i=0;i<m;i++) c[i]=0;
                for(int i=0;i<n;i++) c[x[y[i]]]++;
                for(int i=1;i<m;i++) c[i]+=c[i-1];
                for(int i=n-1;i>= 0; i--) sa[--c[x[y[i]]]]=y[i];
                swap(x,y);p =1;x[sa[0]]=0;
                for(int i=1;i<n;i++)
                    x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
                if(p>=n)break ;
                m=p;
            }
            n--;
            for(int i=0;i<=n;i++)Rank[sa[i]]=i;
        }
    }suf;
    char s[M];
    int R[M],B[M],ans[M];
    int n;
    int Find(int x){
        return x>=n?-1:suf.Rank[x];
    }
    int main(){
        while(~scanf("%d%s",&n,s)){
            suf.n=n;
            for(int i=0,j;i<n;i=j+1){
                j=i;
                while(j+1<n&&s[j+1]==s[i])
                    j++;
                for(int k=i;k<=j;k++)
                    R[k]=j+1;
            }
            for(int i=0;i<n;i++)
                cout<<R[i]<<' ';
            cout<<endl;
            ///构建B[1]数组
            int las0=-1,las1=-1;
            for(int i=0;i<n;i++){
                if(s[i]=='a'){
                    if(las0==-1)B[i]=0;
                    else B[i]=i-las0;
                    las0=i;
                }
                else{
                    if(las1==-1)B[i]=0;
                    else B[i]=i-las1;
                    las1=i;
                }
            }
    
            suf.gao(B,n+3);
            for(int i=0;i<n;i++)
                ans[i]=i;
    
            sort(ans,ans+n,[&](int X,int Y){
                if(R[X]-X!=R[Y]-Y)
                    return R[X]-X<R[Y]-Y;
                if(R[X]>=n||R[Y]>=n)///假设只有有一个R[X]满足>=n,那么它必然在B中全“0”,而另一个至少有一个非“0”,所以以>=n为优先级
                    return R[X]>=n;///而把他单独取出来的原因是它不能单纯地通过B[1]的后缀数组求出,理由也很简单:若只有有一个R[X]满足>=n,那么它实际的字典序会“破坏”B[1]的后缀数组
                return Find(R[X]+1)<Find(R[Y]+1);
            });
            for(int i=0;i<n;i++)
                printf("%d ",ans[i]+1);
            printf("
    ");
        }
        return 0;
    }
    View Code
  • 相关阅读:
    以太坊虚拟机介绍
    以太坊源码学习 – EVM
    Visual Studio Code 常用快捷键
    深入了解以太坊虚拟机第4部分——ABI编码外部方法调用的方式
    深入了解以太坊虚拟机第3部分——动态数据类型的表示方法
    深入了解以太坊虚拟机第2部分——固定长度数据类型的表示方法
    深入了解以太坊虚拟机
    选取文档元素的方法
    flex布局
    什么是功能需求设计文档
  • 原文地址:https://www.cnblogs.com/starve/p/13325116.html
Copyright © 2020-2023  润新知