BZOJ 1031: [JSOI2007]字元加密Cipher 字尾陣列

just_sort發表於2017-01-19

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。

解題思路: 題意中所謂的順序,其實就是把字串複製一遍後的sa值,每個字串的最後一個字元對應的就是s[sa[i]+n−1]。。

程式碼如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 800005;
char s1[maxn];
char s[maxn];
int r[maxn];
int sa[maxn];
int t1[maxn],t2[maxn],c[maxn];
void build_sa(int s[],int n,int m)
{
    int i,j,p,*x=t1,*y=t2;
    for(i=0;i<m;i++)c[i]=0;
    for(i=0;i<n;i++)c[x[i]=s[i]]++;
    for(i=1;i<m;i++)c[i]+=c[i-1];
    for(i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
    for(j=1;j<=n;j<<=1)
    {
        p=0;
        for(i=n-j;i<n;i++)y[p++]=i;
        for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
        for(i=0;i<m;i++)c[i]=0;
        for(i=0;i<n;i++)c[x[y[i]]]++;
        for(i=1;i<m;i++)c[i]+=c[i-1];
        for(i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
        swap(x,y);
        p=1;x[sa[0]]=0;
        for(i=1;i<n;i++)
            x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+j]==y[sa[i]+j]?p-1:p++;
        if(p>=n)break;
        m=p;
    }
}

int main(){
    gets(s1);
    strcat(s, s1);
    strcat(s, s1);
    int n = strlen(s);
    for(int i  =0; i <= n; i++){
        r[i] = s[i];
    }
    int len = strlen(s1);
    build_sa(r, n, 128);
    for(int i = 0; i < n; i++){
        if(sa[i] < len){
            printf("%c", s[sa[i] + len - 1]);
        }
    }
    printf("\n");
    return 0;
}

相關文章