Lazy loaded image
难题解惑(2025.11.03
字数 3328阅读时长 9 分钟
2025-11-3
2025-11-3
password
comment
type
status
date
slug
summary
tags
category
icon
😀
前言 在学习 C / C++ 语言的过程中,我们难免会遇到难以下手的问题。开展本栏目仅为帮助各位同学更好地理解题目、感悟题目,进而能够独立思考,取得进步。
 

📝 原题再现

选自信息学院 ITC 平台 “第二次作业(理论)”第2题

问题描述

一种Playfair密码变种加密方法如下:首先选择一个密钥单词(pair)(字母不重复,且都为小写字母),然后与字母表中其他字母一起填入至一个5X5的正方形中,填入方法如下:
1.首先按行填入密钥串。
2.然后再按字母序按行填入不在密钥串中的字母。
3.由于正方形中只有25个位置,最后剩下的字母不变换
如果密钥为youandme,则该正方形如下:
y o u a n
d m e b c
f g h i j
k l p q r
s t v w x
在加密一对字母时,如am,在正方形中找到以这两个字母为顶点的矩形(红色字体):
y o u a n
d m e b c
f g h i j
k l p q r
s t v w x
这对字母的加密字母为该矩形的另一对顶点,如本例中为ob。
设计一程序,使用上述方法对输入串进行加密,并输出加密后的字符串。
另外有如下规定:
1、如果最后只剩下一个字母,则不变换,直接放入加密串中;
2、如果一对字母中的两个字母相同,则不变换,直接放入加密串中;
3、如果一对字母中有一个字母不在正方形中,则不变换,直接放入加密串中;
4、如果字母对出现在正方形中的同一行或同一列,如df或hi,则只需简单对调这两个字母,即变换为fd或ih;
5、如果在正方形中能够找到以字母对为顶点的矩形,假如字母对为am,则该矩形的另一对顶点字母中,与a同行的字母应在前面,在上例中应是ob;同样若待变换的字母对为ta,则变换后的字母对应为wo;
6、本程序中输入串均为小写字母,并不含其它字符
解密方法与加密相同,即对加密后的字符串再加密,将得到原始串。

输入形式

从控制台输入两行字符串,第一行为密钥单词(长度小于等于25),第二行为待加密字符串(长度小于等于50),两行字符串末尾都有一个回车换行符,并且两行字符串均为小写字母,不含其它字符

输出形式

在标准输出上输出加密后的字符串。

输入样例

youandme
welcometohangzhou

输出样例

vbrmmomvugnagzguu

样例说明

输入的密钥单词为youandme,形成的正方形如上所示;待加密字符串为welcometohangzhou。在正方形中可以找到以第一对字母we为顶点的矩形,对应另一对顶点字母为vb,因此加密后为vb,同理可找到与字母对lc,et,oh,ho对应的顶点字母对。而字母对om位于上述正方形中的同一列,所以直接以颠倒这两个字母来加密,即为mo,字母对an同理。字母对gz中的z不在上述正方形中,因此原样放到加密串中。最后剩一个字母u也原样输出。

算法提示

1、如果用二维数组保存正方形中的字符,可以先在一维数组中填入密钥单词和不在密钥单词中的字母,然后再把该一维数组中的字符放入二维数组中;
2、在对字母对进行加密时,最好按照上述规定中前五条的顺序进行判断。
 

代码示例

 

详细解析

文件头与命名空间

  • #include <iostream>:引入输入输出库,能使用 cincout
  • #include <string>:引入 std::string 类型。
  • using namespace std;:避免每次写 std::,便于初学者书写(注意:大型工程通常不推荐在头文件中使用)。

findPos 函数(查找字母位置)

  • 这是一个辅助函数,用途:在 5×5 方阵 square 中寻找字符 ch,如果找到就通过引用参数 rowcol 返回它的位置,并返回 true;否则返回 false
  • char square[5][5]:传入一个 5×5 的字符二维数组(密钥表)。
  • int &row, int &col:引用参数,用来把找到的行列坐标回传给调用者。
  • 两层 for 循环遍历方阵每个位置;一旦匹配(square[i][j] == ch),把 i,j 存入 row,colreturn true
  • 遍历结束没找到则 return false
  • 这是为了后面判断某个字母是否“在表中”,并获取其坐标以便根据规则变换。

main 开始:读取输入

  • string key, text;:声明两个字符串变量:
    • key:密钥单词(例如 youandme)。
    • text:待加密字符串(例如 welcometohangzhou)。
  • cin >> key >> text;:从标准输入读取两行(题目保证它们都是小写字母且不含其它字符)。注意:>> 会以空白分隔读取,因此可以按题目要求读取两行。

构建密钥序列(不重复)与辅助数组

  • bool exist[26] = {false};:用来记录每个字母('a''z')是否已经出现在 seq 中。索引 0 对应 'a',索引 25 对应 'z'
  • char seq[26];:临时数组,用来按顺序存放密钥中去重后的字母 + 剩余未出现的字母。长度 26 足够(我们最终只会用前25个)。
  • int len = 0;:记录 seq 当前已填入的元素数目(类似动态数组的大小)。

把密钥中的不重复字母放入 seq

  • for (int i = 0; i < key.size(); i++):遍历密钥字符串每个字符。
  • char ch = key[i];:把当前字符取出来,方便使用。
  • if (!exist[ch - 'a']):检查该字母是否已出现。ch - 'a' 将字符映射到 0..25 的索引。
  • 如果没出现:
    • exist[ch - 'a'] = true;:标记为已出现。
    • seq[len++] = ch;:把字母放到 seq 中,并把 len 增加 1(len++ 是后缀,自增但返回旧值,符合把字放进当前索引然后再加 1 的逻辑)。
这段实现了 密钥去重并按密钥顺序保存

补入剩下未出现的字母(按字母序)

  • 遍历 az,如果某字母在 exist 里标记为未出现,就把它追加到 seq
  • 这样 seq 里就包含了:先密钥去重后的字母,再剩下的字母(按字母升序)。

取前 25 个(丢弃一个字母)

  • 方阵只有 25 格,所以只需要 seq 的前 25 个字母。若 len 超过 25(理论上会是 26),就把 len 设为 25,后面填方阵时仅使用前 25 个。

seq 填入 5×5 方阵

  • char square[5][5];:真正的 5×5 密钥表,用二维数组表示。
  • int index = 0;:用来从 seq 中取字母。
  • 两层 for 按行优先(row-major)把 seq 的前 25 个字母放入 square
    • square[0][0] = seq[0]
    • square[0][1] = seq[1]
    • ...
    • square[4][4] = seq[24]
  • index++ 每次使用后自增,方便按顺序填入。

加密主循环:处理每一对字母

  • string result;:保存最终加密结果。
  • for (int i = 0; i < text.size(); i += 2):每次跳 2 个字符,处理一对字母(text[i]text[i+1])。
  • char a = text[i];:第一个字母。
  • char b = (i + 1 < text.size()) ? text[i + 1] : '\0';:如果存在第二个字母就取它,否则把 b 设为 '\0'(空字符)表示“只剩一个字母”。

规则 1:只剩一个字母(直接原样放入并结束)

  • 如果 b'\0'(说明原字符串长度为奇数且这是最后一个字母),按题目要求不变换,直接放入加密串,然后 break 结束循环。

规则 2:两个字母相同(原样放入)

  • 若成对的两个字母相同(例如 aa),题目规定不变换,所以直接把 ab 加入 result,然后 continue 下一个循环(处理下一个字对)。

查找两字母在方阵中的位置

  • 声明四个整型变量 ra, caa 的行列),rb, cbb 的行列)。
  • 调用 findPos 查找 ab
    • ina:是否找到 a(在表中)。
    • inb:是否找到 b(在表中)。
  • 如果 inainbfalse,说明对应字母不在 5×5 方阵中(例如 z 被丢弃了),需按规则处理。

规则 3:有字母不在表中(原样放入)

  • 若任一字母不在方阵中(inainbfalse),直接把原字对加到结果中,不进行任何变换,然后继续下一对。

规则 4:同行或同列(直接对调顺序)

  • 如果两字母在同一行(ra == rb)或同一列(ca == cb),题目要求直接对调这两个字母(例如 dffdhiih)。
  • 所以将 b 先加入,再加入 a,然后继续下一个字对。

规则 5:构成矩形(找另一对顶点,且与第一个字母同行的在前)

  • 两字母既不同行也不在同一列时,它们构成一个矩形的两个对角顶点。
  • a(ra, ca)b(rb, cb),则矩形的另外两个顶点是 (ra, cb)(rb, ca)
  • 题目规定:与第一个字母同行的字母在前,因此先取 square[ra][cb](与 a 同一行的顶点),再取 square[rb][ca](与 b 同一行的顶点),并加入 result

循环结束与输出

  • 循环结束后,程序将所有处理结果保存在 result 中。
  • cout << result << endl;:把加密后的字符串打印到标准输出。
  • return 0;:程序结束并返回 0 表示成功。
 
上一篇
C语言中的函数指针与回调函数(C14)
下一篇
Python3 基础语法

评论
Loading...