2020牛客多校(一)A题 B-Suffix Array(后缀数组)

题目的含义是将一个字符串的所有后缀按算出来的B函数从小到大按字典序排序

首先观察到题目给定的只有ab两个字符,并且b函数给的是与当前位置之前的最近的相同的字符的位置差值

首先暴力的思想就是对每个后缀算一遍b函数,但是发现这样是超时的,因此考虑能否进行优化

我们观察到B函数会变化的原因是,我们求到某个后缀时,前面的数字是不存在的,因此当前位需要重0开始算

但是一旦后缀中出现过a和b这两个字符,这样的话,这个后缀往后的b函数就和初始字符串是一样的。

因此我们想到,如果可以分两部分进行排序,就可以解决问题,因为后一部分只需要求一次即可。

而前一部分,只可能包含类似aaaab,bbba这种,当两种字符都出现过就到了第二部分

而这种类似的字符串的b函数就是01110,这样两个0之间包含很多1。只需要比长度即可,而后半部分用后缀数组求一下rk值即可

当然有些特殊情况,例如字符串后缀是bbbb这样只出现一种字符,我们只需要在n+1的位置补一下a就行,这并不影响原先的排名。

#include<bits/stdc++.h>
using namespace std;
const int N=2e6+10;
int od[N],rk[N],id[N],cnt[N],sa[N],px[N];
char s[N];
int b[N];
struct node{
    int x,y;
    bool operator <(const node &a) const{
        if(y-x==a.y-a.x){
            return rk[y+1]<rk[a.y+1];
        }
        return y-x<a.y-a.x;
    }
}res[N];
bool cmp(int x,int y,int w){
    return od[x]==od[y]&&od[x+w]==od[y+w];
}
void da(int *s,int n,int m){
    int i;
    for(i=1;i<=n;i++) cnt[i]=0;
    for(i=1;i<=n;i++) cnt[rk[i]=s[i]]++;
    for(i=1;i<=m;i++) cnt[i]+=cnt[i-1];
    for(i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;
    int p;
    for(int w=1;w<n;w<<=1,m=p){
        p=0;
        for(i=n;i>n-w;i--)
            id[++p]=i;
        for(i=1;i<=n;i++)
            if(sa[i]>w)
                id[++p]=sa[i]-w;
        for(i=1;i<=n;i++) cnt[i]=0;
        for(i=1;i<=n;i++) cnt[px[i]=rk[id[i]]]++;
        for(i=1;i<=m;i++) cnt[i]+=cnt[i-1];
        for(i=n;i>=1;i--) sa[cnt[px[i]]--]=id[i];
        for(i=0;i<=n;i++) od[i]=rk[i];
        for(p=0,i=1;i<=n;i++){
            rk[sa[i]]=cmp(sa[i-1],sa[i],w)?p:++p;
        }
    }
}
int main(){
    int n;
    ios::sync_with_stdio(false);
    while(cin>>n){
        cin>>s+1;
        int x=-1;
        int y=-1;
        int i;
        for(i=1;i<=n;i++){
            b[i]=0;
            if(s[i]=='a'){
                if(x!=-1)
                    b[i]=i-x;
                x=i;
            }
            else{
                if(y!=-1)
                    b[i]=i-y;
                y=i;
            }
        }
        da(b,n,n);
        x=n+1,y=n+1;
        for(i=n;i>=1;i--){
            if(s[i]=='a'){
                res[i]={i,y};
                x=i;
            }
            else{
                res[i]={i,x};
                y=i;
            }
        }
        rk[n+1]=-1;
        rk[n+2]=-2;
        sort(res+1,res+1+n);
        for(i=1;i<=n;i++){
            cout<<res[i].x<<" ";
        }
        cout<<endl;
    }
}
View Code

 

posted @ 2020-07-15 14:40  朝暮不思  阅读(137)  评论(0编辑  收藏  举报