BZOJ 题目1031: [JSOI2007]字符加密Cipher(后缀数组sa简单应用)

1031: [JSOI2007]字符加密Cipher

Time Limit: 10 Sec   Memory Limit: 162 MB
Submit: 4318   Solved: 1760
[ Submit][ Status][ Discuss]

Description

喜欢钻研问题的JS 同学,最近又迷上了对加密方法的思考。一天,他突然想出了一种他认为是终极的加密办法:把需要加密的信息排成一圈,显然,它们有很多种不同的读法。例如下图,可以读作:

 

JSOI07 SOI07J OI07JS I07JSO 07JSOI 7JSOI0 把它们按照字符串的大小排序: 07JSOI 7JSOI0 I07JSO JSOI07 OI07JS SOI07J 读出最后一列字符:I0O7SJ,就是加密后的字符串(其实这个加密手段实在很容易破解,鉴于这是突然想出来的,那就^^)。但是,如果想加密的字符串实在太长,你能写一个程序完成这个任务吗?

Input

输入文件包含一行,欲加密的字符串。注意字符串的内容不一定是字母、数字,也可以是符号等。

Output

输出一行,为加密后的字符串。

Sample Input

JSOI07

Sample Output

I0O7SJ

HINT

对于100%的数据字符串的长度不超过100000。

Source

/**************************************************************
    Problem: 1031
    User: kxh1995
    Language: C++
    Result: Accepted
    Time:844 ms
    Memory:5276 kb
****************************************************************/
 
#include<stdio.h>              
#include<string.h>              
#include<algorithm>              
#include<iostream>             
#define min(a,b) (a>b?b:a)          
#define max(a,b) (a>b?a:b)               
using namespace std;            
char str[100010];          
int sa[200010],Rank[200010],rank2[200010],c[200010],*x,*y,s[200010],k;   
void cmp(int n,int sz)        
{        
    int i;        
    memset(c,0,sizeof(c));        
    for(i=0;i<n;i++)        
        c[x[y[i]]]++;        
    for(i=1;i<sz;i++)        
        c[i]+=c[i-1];        
    for(i=n-1;i>=0;i--)        
        sa[--c[x[y[i]]]]=y[i];        
}        
void build_sa(int *s,int n,int sz)        
{        
    x=Rank,y=rank2;        
    int i,j;        
    for(i=0;i<n;i++)        
        x[i]=s[i],y[i]=i;        
    cmp(n,sz);        
    int len;        
    for(len=1;len<n;len<<=1)        
    {        
        int yid=0;        
        for(i=n-len;i<n;i++)        
        {        
            y[yid++]=i;        
        }        
        for(i=0;i<n;i++)        
            if(sa[i]>=len)        
                y[yid++]=sa[i]-len;        
            cmp(n,sz);        
        swap(x,y);        
        x[sa[0]]=yid=0;        
        for(i=1;i<n;i++)        
        {        
            if(y[sa[i-1]]==y[sa[i]]&&sa[i-1]+len<n&&sa[i]+len<n&&y[sa[i-1]+len]==y[sa[i]+len])        
                x[sa[i]]=yid;        
            else       
                x[sa[i]]=++yid;        
        }        
        sz=yid+1;        
        if(sz>=n)        
            break;        
    }        
    for(i=0;i<n;i++)        
        Rank[i]=x[i];        
}        
int main()
{
    while(scanf("%s",str)!=EOF)
    {
        int len=strlen(str);
        int i,j,n=0;
        for(i=0;i<len;i++)
        {
            s[n++]=str[i];
        }
        for(j=0;j<len;j++)
            s[n++]=str[j];
        s[n]=0;
        build_sa(s,n,256);
        int tot=0;
        i=1;
        while(tot<len)
        {
            if(sa[i]<len)
            {
                printf("%c",s[sa[i]+len-1]);
                tot++;
            }
            i++;
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值