2020牛客暑期多校训练营(第一场) A、B-Suffix Array(思维+后缀数组)

题目链接

题面:
在这里插入图片描述

题意:
给定一个只有字符a或者字符b组成的串。
其中B数组 Bi 为字符串中第 i 个字符与其之前的最近的相同的字符的距离。
若其之前没有相同的字符,那么 Bi = 0
对于每一个后缀,求出一个B数组,然后对这些数组排序。

题解:
很显然,字符串的后缀的B数组,并不是整个字符串的B数组的后缀,那么就没有办法直接排序。

考虑对于每一位 i 我们求出C数组,其中C数组 Ci 为字符串中第 i 个字符与其之后的最近的相同的字符的距离,若其之后没有相同的字符,那么Ci = n

显然,字符串后缀的C数组,就是整个字符串的C数组的后缀,若能从C数组上做文章,那么此题可解。

考虑两个位置 i,j
若 Ci = Cj 那么对于从 i,j 开头的后缀来说,要么是 ab…ba(Ci-1个b),要么是ba…ab(Ci-1个a)
他们这些位置的B数组均为----- 00111…1Ci,明显若Ci = Cj,那么去比较C i+1,C j+1即可。

若Ci != Cj ,假设Ci < Cj ,那么有 ab…ba (Ci -1 个b),ab…ba (Cj-1 个b)
以 i 开头的B数组为 0011…11Ci(Ci-2个1),以 j 开头的后缀的B数组 0011…11Cj (Cj-2个1)
因为Ci<Cj,比较两个B数组时,所以Ci一定在Cj之前出现,所以对于B数组来说 i 的B数组,大于 j 的B数组。

C数组的后缀排序就是B数组排序的逆序。

还需要额外考虑一点,就是我们在后缀排序时,对于某个后缀 i 来说,若其是某个后缀 j 的前缀,我们的C数组的后缀排序,i 是排在 j 之前的,那么在我们最终所求的B数组中,i 就是排在 j 之后的,实际上我们应该需要在C的后缀中 i 在 j 之后,我们在N+1的位置加上一个CN+1=N+1即可,这样就不会有后缀是另一个后缀的前缀了,而且处理之后在C后缀中 i 会在 j 之后。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<set>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
//#define lc (cnt<<1)
//#define rc (cnt<<1|1)
#define len(x)  (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
using namespace std;

const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=998244353;
const double eps=1e-8;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=200100;
const int maxm=100100;
const int up=1000;

int sa[maxn],rak[maxn],tax[maxn],tp[maxn],height[maxn];
char str[maxn];
int c[maxn];
int N,M;
void Qsort(void)
{
    for(int i=0;i<=M;i++) tax[i]=0;
    for(int i=1;i<=N;i++) tax[rak[i]]++;
    for(int i=1;i<=M;i++) tax[i]+=tax[i-1];
    for(int i=N;i>=1;i--) sa[tax[rak[tp[i]]]--]=tp[i];
}

bool cmp(int *f,int x,int y,int w)
{
    return f[x]==f[y]&&f[x+w]==f[y+w];
}


void suffixsort(void)
{
    M=N;
    for(int i=1;i<=N;i++) rak[i]=c[i],tp[i]=i;
    Qsort();

    for(int w=1,p=0;p<N;M=p,w<<=1)
    {
        p=0;
        for(int i=1;i<=w;i++) tp[++p]=N-w+i;
        for(int i=1;i<=N;i++) if(sa[i]>w) tp[++p]=sa[i]-w;

        Qsort();
        //swap(tp,rak);
        //这里本来是swap,直接交换两个数组,单组样例不会有问题,但是组数多了就会有问题了。
        for(int i=1;i<=N;i++)
            tp[i]=rak[i];
        rak[sa[1]]=p=1;
        for(int i=2;i<=N;i++)
            rak[sa[i]]=cmp(tp,sa[i],sa[i-1],w)?p:++p;
    }
}

void getheight(void)
{
    int k=0;
    for(int i=1;i<=N;i++)
    {
        if(k) k--;
        int j=sa[rak[i]-1];
        while(str[i+k]==str[j+k]) k++;
        height[rak[i]]=k;
    }
}

int main(void)
{
    while(scanf("%d",&N)!=EOF)
    {
        scanf("%s",str+1);
        int na=0,nb=0;
        for(int i=N;i>=1;i--)
        {
            if(str[i]=='a')
            {
                if(na!=0) c[i]=na-i;
                else c[i]=N;
                na=i;
            }
            else if(str[i]=='b')
            {
                if(nb!=0) c[i]=nb-i;
                else c[i]=N;
                nb=i;
            }
        }
        c[N+1]=N+1;
        N++;
        suffixsort();
        for(int i=N-1;i>=1;i--)
            printf("%d ",sa[i]);
        putchar('\n');
    }
    return 0;
}

解法2:

在这里插入图片描述

通过观察,可以将每个后缀的 B 数组分成两段,分别记为段 A 和段 B(上图中表示为段 A 和段 D )

段 A 的特点是:第一个数字和最后一个数字同为 0,且段 A 中只有这两个 0,段 B 的特点是:是字符串 s 的 B 数组的其中一个后缀

因为只有段 A 中含有 0 ,所以在排序时,段 A 的优先级要高于段 B 的,所以段 A 长度更小的,显然应该排在前面

当段 A 的长度相等时,再考虑段 B ,因为此时的段 B 都是字符串 s 的 B 数组的后缀,所以可以利用后缀数组得到 rk 数组,rk[ i ] 当然就是后缀 i 的排名了

当然存在特殊情况,就是如果段 B 为空时,此时显然段 B 为空的字典序更小。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<set>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
//#define lc (cnt<<1)
//#define rc (cnt<<1|1)
#define len(x)  (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
using namespace std;

const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=998244353;
const double eps=1e-8;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=200100;
const int maxm=100100;
const int up=1000;

int sa[maxn],rak[maxn],tax[maxn],tp[maxn],height[maxn];
char str[maxn];
int b[maxn];
int N,M;
void Qsort(void)
{
    for(int i=0;i<=M;i++) tax[i]=0;
    for(int i=1;i<=N;i++) tax[rak[i]]++;
    for(int i=1;i<=M;i++) tax[i]+=tax[i-1];
    for(int i=N;i>=1;i--) sa[tax[rak[tp[i]]]--]=tp[i];
}

bool cmp(int *f,int x,int y,int w)
{
    return f[x]==f[y]&&f[x+w]==f[y+w];
}


void suffixsort(void)
{
    M=N;
    for(int i=1;i<=N;i++) rak[i]=b[i],tp[i]=i;
    Qsort();

    for(int w=1,p=0;p<N;M=p,w<<=1)
    {
        p=0;
        for(int i=1;i<=w;i++) tp[++p]=N-w+i;
        for(int i=1;i<=N;i++) if(sa[i]>w) tp[++p]=sa[i]-w;

        Qsort();
        //swap(tp,rak);
        for(int i=1;i<=N;i++)
            tp[i]=rak[i];
        rak[sa[1]]=p=1;
        for(int i=2;i<=N;i++)
            rak[sa[i]]=cmp(tp,sa[i],sa[i-1],w)?p:++p;
    }
}

struct node
{
    int id,lena,idb;
    node(){}
    node(int a,int b,int c)
    {
        id=a,lena=b,idb=c;
    }
    friend bool operator < (const node &a,const node &b)
    {
        if(a.lena!=b.lena) return a.lena<b.lena;
        else if(a.idb<=N&&b.idb<=N) return rak[a.idb]<rak[b.idb];
        else return a.idb>b.idb;
    }

}s[maxn];


int main(void)
{
    while(scanf("%d",&N)!=EOF)
    {
        scanf("%s",str+1);
        int na=0,nb=0;
        for(int i=1;i<=N;i++)
        {
            if(str[i]=='a')
            {
                if(na!=0) b[i]=i-na+1;
                else b[i]=1;
                na=i;
            }
            else if(str[i]=='b')
            {
                if(nb!=0) b[i]=i-nb+1;
                else b[i]=1;
                nb=i;
            }
        }
        suffixsort();
        na=N+1,nb=N+1;
        //对于A部分为01111的,我们需要给他补上一位,A部分视为011110,那么b的id就应该是N+2
        //对于A部分为01110的,且B部分没有的,那么B的id就是N+1
        //对于N+1,和N+2的rank我们并没有求出来,若排序时a.idb<=N&&b.idb<=N 不成立,那么idb越大的越小
        for(int i=N;i>=1;i--)
        {
            if(str[i]=='a')
            {
                if(nb==N+1) s[i]=node(i,N-i+2,nb+1);
                else s[i]=node(i,nb-i+1,nb+1);
                na=i;
            }
            else if(str[i]=='b')
            {
                if(na==N+1) s[i]=node(i,N-i+2,na+1);
                else s[i]=node(i,na-i+1,na+1);
                nb=i;
            }
        }
        sort(s+1,s+N+1);
        for(int i=1;i<=N;i++)
            printf("%d ",s[i].id);
        putchar('\n');
    }
    return 0;
}








  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值