题目描述

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

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

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

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

输入输出样例
输入 #1复制

JSOI07

输出 #1复制

I0O7SJ

说明/提示
对于40%的数据字符串的长度不超过10000。

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

题解:

样例分析:
JSOI07排序后

 07JSOI
 7JSOI0 
 I07JSO 
 JSOI07 
 OI07JS 
 SOI07J 

每一个字符串取最后一位:I0O7SJ
既然涉及了字符串的排序,就应该想到后缀数组,我们想想这个题,要求按照字符串的大小对各个排列的字符串进行排序。各个排列的字符串我们完全可以用后缀字符串来表示,再用样例分析:
JSOI07的后缀字符串有:

JS0I07
SOI07
Oi07
i07
07
7 

按照大小排序后: 
07
7
i07
JS0I07
Oi07
SOI07

每一列的最后一位为什么没有?别急,因为字符串是一个环状,所以每一列的最后一位也就是第一位的前一位,所以根据第一位向前推就行
答案还是我们要的I0O7SJ
但是真的就这样做没有问题吗?
当s=“bnabn”
我们会发现后缀字符串bn会排在bnabn前面,但是bn对应的是bnbna,应该是小于bnabn的,因为我们用的后缀字符串,导致缺失的串会影响结果,那该怎么办?
有一个小技巧,我们将s变成原来的两倍
s=“bnabn”+“bnabn”=bnabnbnabn
这样的话,我们取s的后缀字符串就会包含所有通过s变化的串,但是同时也会多出一些部分,会影响结果吗?
并不会
我们只需要观察所有sa[i] ≤n的,而对于这一部分,我们在前n位上一定是有序的,后面的一定不会对前n位的顺序造成影响,这也是我们需要的部分

代码:

连续提交三次都未果?洛谷炸了??

#include <bits/stdc++.h>
using namespace std;
char ss[1000005];
int c[1000005],Rank[1000005],K,N,M,SA[1000005],Temp[1000005],a[1000005];
void Build(int x)
{
    for (int i=0;i<=x;i++)
      c[i]=0;
    for (int i=1;i<=N;i++)
      c[Rank[Temp[i]]]++;
    for (int i=1;i<=x;i++)
      c[i]+=c[i-1];
    for (int i=N;i>=1;i--)
      SA[c[Rank[Temp[i]]]--]=Temp[i];
}
void BuildSA()
{
   for (int i=1;i<=N;i++)
     Rank[i]=a[i],Temp[i]=i;
   Build(M=300);
   for (int T=1;T<N;T*=2)
     {
    int p=0;
    for (int i=N-T+1;i<=N;i++)
      Temp[++p]=i;
    for (int i=1;i<=N;i++)
      if (SA[i]>T) 
        Temp[++p]=SA[i]-T;
    Build(M=p);
    swap(Rank,Temp);
    Rank[SA[1]]=1;
    p=1;
    for (int i=2;i<=N;i++)
        {
        if (Temp[SA[i]]==Temp[SA[i-1]]&&Temp[SA[i]+T]==Temp[SA[i-1]+T]) Rank[SA[i]]=p;
        else Rank[SA[i]]=++p;
        }
    }
}
int main()
{
    scanf("%s",ss+1);
    N=strlen(ss+1);
    for (int i=1;i<=N;i++)
    a[i]=ss[i],a[i+N]=a[i],ss[i+N]=ss[i];//把这个字符串复读一遍[误]
    N*=2;
    BuildSA();
    for (int i=1;i<=N;i++)
      if (SA[i]<=(N/2))
       cout<<ss[SA[i]+N/2-1];
    return 0;  
}