洛谷P4051 [JSOI2007]字符加密 后缀数组

题目链接:https://www.luogu.org/problemnew/show/P4051

思路:我们联想求后缀数组sa的过程,发现我们在求y数组的时候(第二关键字,下标为第二关键字的排位,值为合并之后关键字的位置),对于那些没有第二关键字的部分,我们都是直接补0,让这部分的第二关键字排在最前面,但是因为这道题是一个环,所以不存在没有第二关键字的情况,所以我们只需要在板子上面改点东西(求第二关键字的部分改一下),用取模来把范围控制在1到n之间就可以了。

这里因为可能有空格,所以我用gets来读取字符串了,把初始化为256,保险。

代码:

#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<map>
#include<stack>
#include<cmath>
#include<vector>
#include<set>
#include<cstdio>
#include<string>
#include<deque> 
using namespace std;
typedef long long LL;
#define eps 1e-8
#define INF 0x3f3f3f3f
#define maxn 100005
int c[maxn],x[maxn],y[maxn],sa[maxn];
int n,m,k,t;
char s[maxn];
void output(int n){
    for(int i=1;i<=n;i++){
        int index=(sa[i]+n-2)%n+1;//因为sa的下标表示排位,值表示位置,输出的那个末尾字符就是sa[i]+n-1
        //但是我们要控制范围,所以我们对n取模,取模可能为0,所以加1,
        //然后因为外面加了1,所以我们在括号里面减1,所以就是n-2了 
        printf("%c",s[index]);
    }
    printf("\n");
}
void build_sa(int n){
    m=256;
    for(int i=1;i<=m;i++) c[i]=0;
    for(int i=1;i<=n;i++) c[x[i]=s[i]]++;
    for(int i=2;i<=m;i++) c[i]+=c[i-1];
    for(int i=n;i>=1;i--) sa[c[x[i]]--]=i;
    
    for(int k=1;k<=n;k<<=1){
        int num=0;
        //去掉了补0的那部分,因为都存在第二关键字 
        for(int i=1;i<=n;i++) 
        y[++num]=(sa[i]-k+n-1)%n+1;//我是下标从1开始,所以用取模操作把范围控制一下 
        // 对n取模加1,在括号里面减1,这样就抵消了 
        
        for(int i=1;i<=m;i++) c[i]=0;
        for(int i=1;i<=n;i++) c[x[i]]++;
        for(int i=2;i<=m;i++) c[i]+=c[i-1];
        for(int i=n;i>=1;i--) sa[c[x[y[i]]]--]=y[i],y[i]=0;
        
        swap(x,y);
        num=1,x[sa[1]]=1;
        for(int i=2;i<=n;i++)
        x[sa[i]]=y[sa[i]]==y[sa[i-1]]&&y[(sa[i]+k-1)%n+1]==y[(sa[i-1]+k-1)%n+1]?num:++num;
        
        if(num>=n) break;
        m=num;
    }
    output(n);
}
int main()
{
    while(gets(s+1)){
        int len=strlen(s+1);
        build_sa(len);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/6262369sss/p/10320439.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值