转载请注明出处:優YoU http://user.qzone.qq.com/289065406/blog/1299062729
PS:本题稍微说一下题意(当时有点发牢骚的感觉,O(∩_∩)O哈哈~)
一种我认为是比较符合现实的解题思路,但是总是Wrong Answer
咋看之下确实是被题目忽悠了,一般思路都是先对置换解密,再对乱序解密,但是题目所给出的乱序码只有10个,<2, 1, 5, 4, 3, 7, 6, 10, 9, 8>,输入要求却是不大于100的字符串,这里就显示出了矛盾所在:没有10之后的乱序码根本无法解密!!
初看之下这道题目确实是无理取闹,但是从矛盾中同时也给予了我们提示:我们一开始的思路就是错误的!!
试想想,实际中对于一段密文,即使我们知道它是使用了凯撒加密(就是置换加密)和乱序加密的组合,但是我们却很难得出乱序加密的“顺序”,因为它很可能是随机的!
但是凯撒加密这一环确实可以被破解出来 的,通过循环!因为凯撒加密一开始就脱离不了alphabet!而且对于每一个字母它的置换规则都是一样的,例如A→C,是A往后+2所得,那么B的加密也只能+2,B→D,在alphabet末尾的字母就要卷回alphabet开头。那么通过循环去测试A+i的数字i,也不过是26次而已!
那么乱序加密又如何破解?答案是基本没有办法,除非知道了乱序码。但是可以通过间接手段去验证。下面将叙述。
本题其实已经给出了凯撒加密的数字i,就是i=1,这样就省去破解凯撒加密的时间了。
输入密文和猜测的明文后,先破解密文中凯撒加密的部分,那么现在的密文和猜测的明文区别就在于顺序性!
虽然我们不知道乱序码,但是正因为不知道乱序码,我们有更加简单的方法去判定密文和明文是否一致!我们把密文和明文都视为乱序,然后分别对它们的所有字母进行排序!
现在只需要比较排序后的两段文字,就可以判定密文和猜测的明文是否一致了。
正确解题思路
虽然方法更简单,但是正常人很难想到会是这个思路
不必理会凯撒加密和乱序加密,默认都是不知道解码方法。
那么唯一判断 密文 和 猜测的明文 是否一致的方法就是 把出现频数相同的字母归结为一类(而不必理会组成这一类中的各个字母类型是否一致,但是字母的数量必须一致,例如密文中ABCD都分别出现一次,把他们归为一类,明文中BNUJ也都分别出现一次,把他们也归结为一类,那么这时认为ABCD和BNUJ这两类是等价的;但若密文中ABCD都分别出现一次,而明文中出现一次的只有 KIO三个字母,那么 ABCD和KIO是不等价的),并将出现的次数进行排序,再比较两个频数序列是否相同即可(比较的是频数列,而不是字母列),相同说明可由明文串经变换得出密文串(看这口气好像是说“至于怎么变换出来就不是我们该考虑的问题了,我们管不着!” )
1 //Memory Time
2 //188K 16MS
3
4 #include<iostream>
5 #include<string>
6 #include<algorithm>
7 using namespace std;
8
9 int main(void)
10 {
11 int i;
12 int cipher[26],clear[26];
13 memset(cipher,0,sizeof(cipher)); //对长度为sizeof(cipher)=int*26=104的连续内存空间cipher全部元素初始化为0
14 memset(clear,0,sizeof(clear));
15 string input;
16 cin>>input;
17 for(i=0;i<input.length();i++)
18 {
19 cipher[input[i]-'A']++;
20 }
21 cin>>input;
22 for(i=0;i<input.length();i++)
23 {
24 clear[input[i]-'A']++;
25 }
26 sort(cipher,cipher+26); // 标准库自带排序函数sort(ip_start,ip_end) 对某连续的地址段的对象内容进行升序快排(从小到大),<algorithm>
27 sort(clear,clear+26); //亦即sort(ip_start+m,ip_start+n),其中为从ip_start+m开始(包括ip_start+m)对第n到第m个对象进行排序
28 for(i=0;i<26;i++)
29 if(cipher[i]!=clear[i])
30 {
31 cout<<"NO"<<endl;
32 return 0;
33 }
34 cout<<"YES"<<endl;
35 return 0;
36 }