题目拆解
给定一本「密码本」book[10](book[i] 表示数字 i 加密后变成什么)和 n 个待加密数字,把每个数字按密码本替换成对应的新数字并输出。
已知约束有三层。输入共 3 行:第一行为整数 n(待加密数字个数,不超过 20000);第二行为 n 个 0-9 的整数;第三行为 10 个整数(密码本),第 1 个表示 0 的密文、第 2 个表示 1 的密文,第 10 个表示 9 的密文。密码本中数字不重复且均为 0-9(即一组 0-9 的排列)。隐含约束:待加密数字与密码本数字都只取 0-9,因此 a[i] 可直接作为 book 的下标;密码本是 0-9 的一个排列,映射一一对应;输出数字之间以空格分隔;n 可达 20000,需注意 I/O 效率与数组/向量大小,但仍在 int 与常规内存范围内。
知识点
▪ 数组作为映射表:用下标直接查表(book[i] 表示 i 的映射值) ▪ 一维数组 / vector 的读入与遍历 ▪ 多行整数的读取顺序:先读 n,再读 n 个数字,最后读 10 个密码本数字 ▪ I/O 效率:ios::sync_with_stdio(false) 在 n 较大时防止超时 ▪ 排列与一一映射:密码本是 0-9 的排列,保证每个数字都有唯一密文
思路分析
这道题的核心是「查表法」。密码本给出的是「数字 i 变成什么」,恰好是一个以数字本身为下标的映射:把它放进长度为 10 的数组 book,那么 book[0] 就是 0 的密文、book[1] 就是 1 的密文,依此类推。待加密的每一个数字 d 都落在 0 到 9,正好能当作数组下标直接取出它的密文 book[d],不需要任何判断或计算。
读入时按题目顺序来:先读 n,再读 n 个待加密数字(存进 vector 或数组),然后读 10 个密码本数字。最后从左到右遍历每个待加密数字,输出 book[对应值] 即可,注意数字之间用空格分隔、不要在末尾多打一个空格。复杂度上只需遍历一遍 n 个数字,n 不超过 20000,时间与空间都很宽松;因为 n 可能偏大,养成用 ios::sync_with_stdio(false) 关掉流同步的习惯能稳妥防超时。
算法描述
关键思路: 用长度为 10 的 book 数组存密码本,待加密数字作下标查表映射;数字之间用空格分隔、末尾不补多余空格。
循环读入每组用例:while(cin >> n)
读入 n 个待加密数字到数组 a(下标 0..n-1)
读入 10 个密码本数字到 book(下标 0..9,book[i] 表示数字 i 的密文)
对 i = 0..n-1:输出 book[a[i]],数字之间用空格分隔(i>0 时先输出一个空格)
编程实现
1// 4189【GESP2606 三级】加密
2// 规则:有一本密码本 book[10],book[i] 表示数字 i 加密后变成哪个数字。
3// 读入 n 个待加密数字,每个数字 d 替换成 book[d] 输出,数字之间用空格分隔。
4// 思路:把密码本存进长度为 10 的数组,待加密的数字正好当作下标去查表,
5// 这是一种典型的「查表法 / 下标映射」,做法直观且不会出错。
6#include<iostream>
7#include<vector>
8using namespace std;
9
10intmain() {
11ios::sync_with_stdio(false); // 关闭 C/C++ 流同步,关掉后 cin 更快
12cin.tie(nullptr); // 解绑 cin 与 cout,进一步提速
13
14int n;
15// 支持读入多组用例(在线判题通常只喂一组,这样写更稳健,EOF 处自然退出)
16while (cin >> n) {
17// 读入 n 个待加密的数字
18vector<int> a(n);
19for (int i = 0; i < n; i++) {
20cin >> a[i];
21 }
22
23// 读入密码本:第 i 个数字表示 i 加密后变成什么
24int book[10];
25for (int i = 0; i < 10; i++) {
26cin >> book[i];
27 }
28
29// 按密码本逐个映射并输出,数字之间用空格分隔(末尾不补多余空格)
30for (int i = 0; i < n; i++) {
31if (i > 0) cout << ' ';
32cout << book[a[i]]; // a[i] 在 0..9,正好当作下标查表
33 }
34cout << '\n';
35 }
36return0;
37}
运行调试
正确性:样例 n=7、待加密 0 2 0 3 4 1 9、密码本 9 0 1 2 3 4 5 6 7 8,输出 9 1 9 2 3 0 8,与题目样例一致;另以独立 Python oracle(同样查表映射)对随机用例对照编译后二进制,含 n=0、最长 20000、密码本为 0-9 排列等 2006 组用例全部一致。
效率:时间 O(n)、空间 O(n),n≤20000,远低于瓶颈;使用 ios::sync_with_stdio(false) 关闭流同步防超时。
规范性:用数组下标直接查表,避免复杂分支;输出用「i>0 先打空格」控制格式,末尾无多余空格,符合样例格式。
边界:待加密数字全为同一值(如全 0)、密码本为逆序排列(9 8 7 … 0)等极端情形均正确;n=0(无待加密数字)整体输出空行仍成立;n=20000 最大规模在内存与时限内正常。待加密数字作 book 下标时恒在 0..9,不会越界。
答疑解惑
苏格拉底式追问:
密码本 book[i] 的下标 i 代表什么?为什么待加密的数字能直接当作下标? 如果不用数组存密码本,而是用 10 个 if 判断数字 0..9 分别变成什么,两种方式在可读性和扩展性上有什么差别? 输出时为什么建议用「i>0 才先打一个空格」而不是「每个数字后都打空格」?
易错提醒:
密码本读入顺序搞反:密码本第 1 个数是 0 的密文、第 2 个是 1 的密文,必须按 i=0..9 依次读入 book[i],不能错位。 下标越界或映射错乱:要把「待加密的数字 d」当作下标查 book[d],而不是把 book 的位置当数字;d 恒在 0..9,但数组大小至少开 10。 多打/少打空格:题目要求数字间空格分隔、末尾无多余空格,逐个输出再补空格容易尾随空格被判格式错误;用「首元素前不补空格」最稳。 I/O 超时:n 可达 20000,若用未关同步的 cin 反复读且输出量大,极端情况下可能偏慢;用 ios::sync_with_stdio(false) 更稳妥。
复盘迁移:本题是数组映射(查表法)的入门原型:把「规则」本身存进数组,用输入值当下标去取结果。同类套路(如字母映射、桶计数、进制转换查表)都先想「能不能开一个小数组当下标表」。注意密码本是 0-9 的排列这一条件只保证映射一一对应,算法本身并不依赖它,即使有重复也能照常查表。
自测题 1:待加密数字 0 2 0 3 4 1 9,密码本 9 0 1 2 3 4 5 6 7 8,加密后结果是?
参考答案
9 1 9 2 3 0 8。book[0]=9、book[2]=1、book[0]=9、book[3]=2、book[4]=3、book[1]=0、book[9]=8,依次按空格拼接,与样例一致。
自测题 2:为什么密码本用一个长度为 10 的数组 book 存,而不用 10 个独立变量?
参考答案
因为待加密的数字 d 正好落在 0..9,能直接当作数组下标 book[d] 取出密文,这是查表法;用 10 个独立变量就需要一堆 if/else 分支判断 d 是多少,既啰嗦又易错,数组下标映射一步到位。
自测题 3:若 n=20000 且所有待加密数字都是 5,密码本为 0-9 的一个排列,程序能否正确且高效运行?
参考答案
能。每个数字 5 查出 book[5] 后输出,共输出 20000 个相同值,时间 O(n) 线性扫描、空间仅存一个 vector,内存约 80KB,远低于 64MB 与 1s 限制;配合关闭流同步可进一步防超时。