Description:
对于一个长度为N的字符串,我们在字符串的末尾添加一个特殊的字符”.”。之后将字符串视为一个环,从位置1,2,3,…,N+1为起点读出N+1个字符,就能得到N+1个字符串。
比如对于字符串“ABCAAA”,我们可以得到这N+1个串:
ABCAAA.
BCAAA.A
CAAA.AB
AAA.ABC
AA.ABCA
A.ABCAA
.ABCAAA
接着我们对得到的这N+1个串按字典序从小到大进行排序(注意特殊字符“.”的字典序小于任何其他的字符)结果如下:
.ABCAAA
A.ABCAA
AA.ABCA
AAA.ABC
ABCAAA.
BCAAA.A
CAAA.AB
最后,将排序好的N+1个串的最后一个字符取出,按照顺序排成一个新的字符串,也就是上面这个表的最后一列,就是加密后的密文“AAAC.AB”。
请通过加密后的密文求出加密前的字符串。
思路:
看到这题的解法后简直震惊了,代码竟然如此简短。。。想了我好久,终于好像是想明白了。
首先我们先来考虑没有重复字符的情况可能会更好理解。
我们可以考虑一位位地来确定我们的答案,接在
.
.
后面的数字一定是第一个字符,所以以结尾的字符串的排名一定是以第一个字符串开头的排名。排名已知,又没有重复,这样我们就可以确定了第一个字符。确定了第一个字符又可以用类似的方法来确定第二个字符,以此类推。
但是如果有重复的字符该怎么办呢?此时我们不可以确定的就是排名为p的字符串的开头的字母是什么,发现这是字典序比较,先比前面再比后面,所以从一开始输入的序列中我们就可以得到经过字典序之后的排名(这里要考虑序列是循环的性质)。
所以我们为了得到按照字典序来排序的字符串的首字母,只需要按照权值第一关键字,下标第二关键字排序即可。
/*=======================
* Author : ylsoi
* Problem : password
* Algorithm : sort
* Time : 2018.4.26
*======================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
void File(){
freopen("[bzoj4104]password.in","r",stdin);
freopen("[bzoj4104]password.out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define ll long long
#define inf (0x3f3f3f3f)
const int maxn=2e5+10;
int n,m,a[maxn];
struct node{
int va,id;
bool operator < (const node & tt) const {
if(va!=tt.va)return va<tt.va;
return id<tt.id;
}
}b[maxn];
int main(){
File();
scanf("%d%d",&n,&m);
REP(i,1,n+1){
scanf("%d",&a[i]);
b[i].va=a[i];
b[i].id=i;
}
sort(b+1,b+n+2);
int p=b[1].id;
REP(i,1,n){
printf("%d ",b[p].va);
p=b[p].id;
}
return 0;
}