一种比较简单的实现方法

· · 题解

我在上小学时候的睡前读物就是《福尔摩斯探案集》,对“跳舞小人”那一章记忆犹新(当时看标题觉得可能是什么恐怖故事,硬是留到了最后才看)。

福尔摩斯解密“跳舞小人”密码的时候,提到了英文中最受欢迎的字母就是‘e’,后来看 《怪诞小镇》 的时候经常用这种方法去尝试破译(虽然没有成功过)。

好了,看这道题。

既然是凯撒加密法,那么我们只要找到密文中对应‘E’的字母,我们就可以找到全部的密码表。

观察这道题的输入,个人认为START和END有些不太必要,可能是为了进一步降低难度。我们使用gets()函数进行读取时,完全可以忽略这两个关键词的影响,跳过就好了,对我们来说真正有用的只有安全词——ENDOFINPUT。

经过测试发现,输入结尾可能是'\r\n'和'\n'。如果结尾是'\r',gets()是会读入的,当然可以用scanf的[ ]去解决。但其实我们只需要在比较判断时,使用 strncmp() 解决就可以了。

还有,当密文出现多个相等最大值时,取后者!

#define _CRT_SECURE_NO_DEPRECATE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int e[30] = { 0 };
int upper(char s[])//字母替换为大写
{
    for (int i = 0; s[i] != '\0'; i++)
        if (s[i] >= 'a' && s[i] <= 'z')
            s[i] = s[i] - 'a' + 'A';
    return 0;
}
int finde(char s[])//找到密文‘E’
{
    int key = 0, max = 0;
    for (int i = 0; s[i] != '\0'; i++)
        if (s[i] >= 'A' && s[i] <= 'Z')
        {
            e[s[i] - 'A']++;
            if (e[s[i] - 'A'] >= max)
            {
                max = e[s[i] - 'A'];
                key = s[i];
            }
        }
    key -= 'E';
    return key;
}
int main()
{
    char s[10010] = "\0";
    int key;
    while (gets(s))
    {
        if (strncmp(s, "ENDOFINPUT", 10) == 0)
            break;
        else if (strncmp(s, "START", 5) == 0 || strncmp(s, "END", 3) == 0)
            continue;
        else
        {
            upper(s);
            key = finde(s);
        }
        for (int i = 0; s[i] != '\0'; i++)
        {
            if (s[i] >= 'A' && s[i] <= 'Z')//简单的处理+输出
            {
                if (s[i] - key > 'Z')
                    printf("%c", s[i] - key - 26);
                else if (s[i] - key < 'A')
                    printf("%c", s[i] - key + 26);
                else
                    printf("%c", s[i] - key);
            }
            else
                printf("%c", s[i]);
        }
        printf("\n");
    }
    return 0;
}