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
原文地址:https://www.cnblogs.com/starve/p/13325116.html