2020牛客暑期多校训练营(第一场)B-Suffix Array 后缀数组结论

题目传送门:B-Suffix Array

题意:一个字符串S的B数组定义为,如果存在Sj==Si,j<i,则B[i]=i-j,否则B[i]=0。给定字符串S,给出它所有后缀按它们的B数组排序后的顺序。

我不会。种下一片草地艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹

根据题解就是,对于这种二元串(只含有两种元素),存在一个C数组,C数组的定义是如果存在Sj==Si,j>i,则C[i]=i-j,否则C[i]=lens(S的长度)。

对于B数组来说,S后缀的B数组不一定是它的B数组的后缀,但对C数组,S后缀的C数组一定是它的C数组的后缀。

而按照B数组排序,和按照C数组排序,得到的顺序是相反的。大佬的证明

所以我们只需要把S的C数组求出后缀数组的sa,再倒序输出就好。

小细节处理,在原本B中,像0,00,000,中肯定是越短的越前面,而C中n,nn,nnn我们则需要让越短的在越后面,做法就是在末尾添加n+1即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+11;
char s[N]; 
int a[N];
int sa[N],ra[N];
int cntr[N],tsa[N];
void sasort(int n,int m){
    for(int i=0;i<=m;i++) cntr[i]=0;
    for(int i=1;i<=n;i++) cntr[ra[i]]++;
    for(int i=1;i<=m;i++) cntr[i]+=cntr[i-1];
    for(int i=n;i>=1;i--) sa[cntr[ra[tsa[i]]]--]=tsa[i];
}
void getsa(int n){
    int m=n;
    for(int i=1;i<=n;i++) ra[i]=a[i],tsa[i]=i;
    sasort(n,m);
    for(int l=1;l<=n;l<<=1){
        int num=0;
        for(int i=n-l+1;i<=n;i++) tsa[++num]=i;
        for(int i=1;i<=n;i++) if(sa[i]>l) tsa[++num]=sa[i]-l; 
        sasort(n,m);
        memcpy(tsa,ra,(n+1)*sizeof(int));
        ra[sa[1]]=1;
        for(int i=2;i<=n;i++){
            ra[sa[i]]=ra[sa[i-1]];
            if(tsa[sa[i]]!=tsa[sa[i-1]]||tsa[sa[i]+l]!=tsa[sa[i-1]+l]) ra[sa[i]]++;
        }
        m=ra[sa[n]];
        if(m>=n) break;
    }
}
int main(){
    int n;
    while(~scanf("%d",&n)){
        scanf("%s",s+1);
        int ba=0,bb=0;
        for(int i=n;i>=1;i--){
            if(s[i]=='a'){
                if(!ba) a[i]=n;
                else a[i]=ba-i;
                ba=i;
            }else{
                if(!bb) a[i]=n;
                else a[i]=bb-i;
                bb=i;
            }
        }
        ++n;a[n]=n;
        getsa(n);
        for(int i=n-1;i>=1;i--) printf("%d%c",sa[i]," \n"[i==1]);
    }
    return 0;
} 
证明啥不会,题解第一名

 

posted @ 2020-07-17 16:11  新之守护者  阅读(201)  评论(0编辑  收藏  举报