bzoj1031: [JSOI2007]字符加密Cipher(后缀数组)

题目传送门
题目挺不错的。
赤果果的后缀数组啊。
我好像只会后缀数组作后缀排名了。
真的菜。(我要变强!!)

因为他的字符串排成了一圈了。所以说我们应该长度为两倍才行。
比如:
abcd。
那么就会有四个串。
abcd,bcda,cdab,dabc。
这样很容易就可以看出我们应该把字符串变成:
abcdabc。
这样的话我们就会有这四个串了。

那么拿新的字符串去跑一遍后缀数组。
那么就会有2*len-1个后缀。len表示原串长度
那么有些字符串很明显要越界了。
所以我们只要不越界的字符串输出最后一个字符就OK了。
比如这个后缀的初始位置为i。
因为每个串的长度都应该为len。
那么我们就要判断i+len-1也就是这个串的结束位置是否越界(<=2*len-1)

然后把最后一个字符都输出出来就OK了吧。

代码实现:

#include<cstdio>
#include<cstring>
using namespace std;
char s[1100000];
int sa1[1100000],sa2[1100000],Rsort[1100000],Rank[1100000],a[1100000],tt[1100000];
char ans[1110000];
void solve(int n,int m) {
    for(int i=1;i<=n;i++) 
        Rank[i]=a[i];
    memset(Rsort,0,sizeof(Rsort));
    for(int i=1;i<=n;i++) 
        Rsort[Rank[i]]++;
    for(int i=1;i<=m;i++) 
        Rsort[i]+=Rsort[i-1];
    for(int i=n;i>=1;i--) 
        sa1[Rsort[Rank[i]]--]=i;
    int p=0,ln=1;
    while(p<n) {
        int k=0;
        for(int i=n-ln+1;i<=n;i++) 
            sa2[++k]=i;
        for(int i=1;i<=n;i++) 
            if(sa1[i]-ln>0) sa2[++k]=sa1[i]-ln;
        memset(Rsort,0,sizeof(Rsort));
        for(int i=1;i<=n;i++) 
            Rsort[Rank[i]]++;
        for(int i=1;i<=m;i++) 
            Rsort[i]+=Rsort[i-1];
        for(int i=n;i>=1;i--) 
            sa1[Rsort[Rank[sa2[i]]]--]=sa2[i];
        for(int i=1;i<=n;i++) 
            tt[i]=Rank[i];
        p=1;
        Rank[sa1[1]]=1;
        for(int i=2;i<=n;i++) {
            if(tt[sa1[i]]!=tt[sa1[i-1]] || tt[sa1[i]+ln]!=tt[sa1[i-1]+ln]) 
                p++;
            Rank[sa1[i]]=p;
        }
        m=p;
        ln*=2;
    }
    int len=0;
    for(int i=1;i<=n;i++) 
        if(sa1[i]+n/2<=n)
        //这里本来写的是sa1[i]+n/2+1-1的,因为2*len-1很明显为基数,那么他除以2的话就会比原长度少1,那么要加上1,后面的-1就是加上长度之后-1的位置就是结尾。
            ans[++len]=s[sa1[i]+n/2];
    for(int i=1;i<=len;i++)
        printf("%c",ans[i]);
    printf("\n");
}
int main() {
    scanf("%s",s+1);
    int len=strlen(s+1);
    for(int i=1;i<=len-1;i++)  //把字符串复制一遍
        s[i+len]=s[i];
    len*=2;len--;
    for(int i=1;i<=len;i++) 
        a[i]=s[i];
    solve(len,300);
    return 0;
}

后缀数组有点玄学,初二的时候第一次学很懵13。
然而现在终于理解了,太开心了!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值