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),两行字符串末尾都有一个回车换行符,并且两行字符串均为小写字母,不含其它字符。
输出形式
在标准输出上输出加密后的字符串。
输入样例
youandmewelcometohangzhou输出样例
vbrmmomvugnagzguu样例说明
输入的密钥单词为youandme,形成的正方形如上所示;待加密字符串为welcometohangzhou。在正方形中可以找到以第一对字母we为顶点的矩形,对应另一对顶点字母为vb,因此加密后为vb,同理可找到与字母对lc,et,oh,ho对应的顶点字母对。而字母对om位于上述正方形中的同一列,所以直接以颠倒这两个字母来加密,即为mo,字母对an同理。字母对gz中的z不在上述正方形中,因此原样放到加密串中。最后剩一个字母u也原样输出。
算法提示
1、如果用二维数组保存正方形中的字符,可以先在一维数组中填入密钥单词和不在密钥单词中的字母,然后再把该一维数组中的字符放入二维数组中;
2、在对字母对进行加密时,最好按照上述规定中前五条的顺序进行判断。
代码示例
详细解析
文件头与命名空间
#include <iostream>:引入输入输出库,能使用cin、cout。
#include <string>:引入std::string类型。
using namespace std;:避免每次写std::,便于初学者书写(注意:大型工程通常不推荐在头文件中使用)。
findPos 函数(查找字母位置)
- 这是一个辅助函数,用途:在 5×5 方阵
square中寻找字符ch,如果找到就通过引用参数row和col返回它的位置,并返回true;否则返回false。
char square[5][5]:传入一个 5×5 的字符二维数组(密钥表)。
int &row, int &col:引用参数,用来把找到的行列坐标回传给调用者。
- 两层
for循环遍历方阵每个位置;一旦匹配(square[i][j] == ch),把i,j存入row,col并return 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 的逻辑)。
这段实现了 密钥去重并按密钥顺序保存。
补入剩下未出现的字母(按字母序)
- 遍历
a到z,如果某字母在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),题目规定不变换,所以直接把a和b加入result,然后continue下一个循环(处理下一个字对)。
查找两字母在方阵中的位置
- 声明四个整型变量
ra, ca(a的行列),rb, cb(b的行列)。
- 调用
findPos查找a和b: ina:是否找到a(在表中)。inb:是否找到b(在表中)。
- 如果
ina或inb是false,说明对应字母不在 5×5 方阵中(例如z被丢弃了),需按规则处理。
规则 3:有字母不在表中(原样放入)
- 若任一字母不在方阵中(
ina或inb为false),直接把原字对加到结果中,不进行任何变换,然后继续下一对。
规则 4:同行或同列(直接对调顺序)
- 如果两字母在同一行(
ra == rb)或同一列(ca == cb),题目要求直接对调这两个字母(例如df→fd或hi→ih)。
- 所以将
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表示成功。
- 作者:计算机类2507班
- 链接:https://learning.lcyteam.me//article/hard-251103
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。








