ACM典型试题--古代密码(二)
1. 题目描述
古罗马帝国有两种简单的加密算法,第一种按照顺序替换,例如把a-y 分别替换成b-z,把z 替换成a,这样可以把VICTORIOUS 替换成WJDUPSJPVT。
第二种是打乱顺序消息的顺序,例如<2, 1, 5, 4, 3, 7, 6, 10, 9, 8>的含义就是把第二个字
符放在第一位,而把第一位的字符放到第二位,然后是第5 个字符,第4 个字符,…,可以
把VICTORIOUS 替换成IVOTCIRSUO。
后来发现同时使用两种算法, 加密效果更好。可以把VICTORIOUS 替换成
JWPUDJSTVP。
题目要求:能不能把第二行中的原文转换为第一行的密文。
输入格式
输入包括两行:第一行为加密后的密文,第二行原文。
输出格式
如果能够按此方法把第二行的原文转换为第一行的密文,则输出 YES,否则输出NO。
输入样例
JWPUDJSTVP
VICTORIOUS
输出样例
YES
2. 题目分析和算法实现
首先,要找出规律,第二种方法只会改变每个字符的位置,但是不会影响每个字符在字符串中出现的次数。例如A 在原来的字符串中出现3 次,那么通过第二种算法它出现的次
数还是3 次。第一种算法虽然改变了字符串的内容,但是有些东西没有变化,例如原来字符
串中的a、b、c 出现的次数分别n1、n2、n3,假设abc 替换def,则d、e、f 出现的次数应
该是n1、n2、n3。所以只要保证相对位置上的字符出现的次数相同即可实现转换。
统计输入信息第一行中每个字符出现的次数。使用长度为26 的数组表示,分别表示字
母A 到字母Z 出现的次数,使用int[] a 表示。
统计输入信息第二行中的每个字符出现的次数。使用长度为26 的数组表示,分别表示
字母A 到字母Z 出现的次数,使用int[] b 表示。
我们循环26 次,第j 次循环中,再循环比较a[(i+j)%26]与b[i]是否相同,如果都相同则
说明能够转换,输出YES 即可,退出外层循环,否则继续循环。如果26 次循环完之后,没有得到结果,输出NO。
3. 问题实现及代码分析
#include<stdio.h>
#include<string.h>
void init(), work(), sort(), print();
int sum1[120], sum2[120], sm1, sm2, count1[120], count2[120], bj;
char s1[120], s2[120];
int main() {
init();
work();
return 0;
}
void init() {
int i;
for(i = 1; i <= 119; ++i) {
sum1[i] = sum2[i] = count1[i] = count2[i] = 0;
}
sm1 = 0;
sm2 = 0;
gets(s1);
gets(s2);
}
void work() {
int i;
for(i = 0 ; i <= strlen(s1) - 1; ++i){
++sum1[s1[i] - 'A' + 1];
++sum2[s2[i] - 'A' + 1];
}
for(i = 1; i <= 26; ++i) {
if(sum1[i])
count1[++sm1] = sum1[i];
if(sum2[i])
count2[++sm2] = sum2[i];
}
if(sm1 != sm2) {
bj = 1;
print();
}
else {
sort();
bj = 2;
for(i = 1; i <= sm1; ++i)
if(count1[i] != count2[i]) {
bj = 1;
break;
}
print();
}
}
void print() {
if(bj == 1) printf("NO\n");
else
printf("YES\n");
}
void sort() {
int i, j, ch;
for(i = 1; i <= sm1; ++i )
for(j = i + 1; j <= sm1; ++j ) {
if(count1[i] > count1[j]) {
ch = count1[i];
count1[i] = count1[j];
count1[j] =ch;
}
if(count2[i] > count2[j]) {
ch = count2[i];
count2[i] = count2[j];
count2[j] =ch;
}
}
}
浙公网安备 33010602011771号