GESP 客观题评测系统

试卷解析 · 理解每一步

普及组 CSP-J 2026 初赛模拟卷 7

CSPJ-2026-R1-Mock-7

试卷解析总览,可直接查看每题答案与解析。

一、单项选择题

第 1 题(单选题,2 分)

计算机内存中的每一个基本单元,都被赋予一个唯一的序号,称为( )。

A.
字节
B.
编号
C.
容量
D.
地址

正确答案D

解析详情
答案 D内存地址
思路与推导
  1. 内存按基本单元组织,处理器通过地址定位要读写的单元;每个单元对应的唯一序号就是地址。

第 2 题(单选题,2 分)

计算 (ABCDEF)16+(FEDCBA)16(ABCDEF)_{16}+(FEDCBA)_{16} 的二进制结果是( )。

A.
(1101010101010101010101001)2(1101010101010101010101001)_2
B.
(1001010101010101010101001)2(1001010101010101010101001)_2
C.
(1101010101010111010101001)2(1101010101010111010101001)_2
D.
(1001011101010101010101001)2(1001011101010101010101001)_2

正确答案A

解析详情
答案 A进制运算
思路与推导
  1. 按十六进制逐位相加得 (ABCDEF)16+(FEDCBA)16=(1AAAAA9)16(ABCDEF)_{16}+(FEDCBA)_{16}=(1AAAAA9)_{16}。

  2. 每个十六进制位换成 4 位二进制,得到 (1101010101010101010101001)2(1101010101010101010101001)_2。

第 3 题(单选题,2 分)

十进制数 −67-67 的八位二进制补码是( )。

A.
(10111100)2(10111100)_2
B.
(10111101)2(10111101)_2
C.
(11000011)2(11000011)_2
D.
(11000010)2(11000010)_2

正确答案B

解析详情
答案 B补码表示
思路与推导
  1. 6767 的八位原码为 0100001101000011;表示 −67-67 时逐位取反得 1011110010111100,再加 11 得 1011110110111101。

第 4 题(单选题,2 分)

12 张扑克牌,分别为黑桃、红心、梅花、方块的 J、Q、K。从中抽取三张牌,如果有对子就能获胜;如果没有对子,有黑桃 K 也能获胜。获胜的概率是( )。

A.
43/5543/55
B.
4/54/5
C.
97/11097/110
D.
46/5546/55

正确答案A

解析详情
答案 A组合计数与对立事件
思路与推导
  1. 总取法为 (123)=220\binom{12}{3}=220。失败必须三种点数各取一张且不含黑桃 K,共 4×4×3=484\times4\times3=48 种。

  2. 获胜概率为 (220−48)/220=43/55(220-48)/220=43/55。

第 5 题(单选题,2 分)

一个链表的节点定义为 `struct node { int data; node* next; };`。该链表中的 `data` 数值从小到大排列。现在想在指针 `node* a` 指向的节点后插入一个 `data` 为 20 的新节点,正确的操作指令是( )。

A.
node* newnode;
a->data = 20;
a->next = newnode;
newnode->data = a->data;
B.
node* newnode;
newnode->data = 20;
a->next = newnode;
a->data = newnode->data;
C.
node* newnode;
newnode->data = 20;
a->next = newnode;
newnode->next = a->next;
D.
node* newnode;
newnode->data = 20;
newnode->next = a->next;
a->next = newnode;

正确答案D

解析详情
答案 D单链表插入
思路与推导
  1. 假定 newnode 已指向分配好的新节点,应先令 newnode->next 保存 a 原来的后继,再令 a->next 指向 newnode。

  2. 这样链条由 a->原后继变为 a->newnode->原后继,不会丢失后半段链表。

第 6 题(单选题,2 分)

24 点游戏指的是用给出的四个数字结合加、减、乘、除运算凑出 24。用 1、4、5、6 算 24 点,下列后缀表达式中正确的是( )。

A.
`1 5 6 / - 4 /`
B.
`5 6 / 1 - 4 /`
C.
`4 1 5 6 / - /`
D.
`4 1 5 6 - / /`

正确答案C

解析详情
答案 C后缀表达式求值
思路与推导
  1. 选项 C 依次计算 5/65/6、1−5/61-5/6,最后计算 4/(1−5/6)=4/(1/6)=244/(1-5/6)=4/(1/6)=24。

第 7 题(单选题,2 分)

在 C++ 语言中,`char K = (char)('J' - 7)`,那么变量 `K` 代表的字母是( )。

A.
`'B'`
B.
`'C'`
C.
`'D'`
D.
`'K'`

正确答案B

解析详情
答案 B字符编码
思路与推导
  1. 英文字母编码连续,'J' 向前移动 7 个位置依次经过 I、H、G、F、E、D,到达 'C',所以 K 表示字符 'C'。

第 8 题(单选题,2 分)

字符 A~F 的出现频率分别为 0.04、0.09、0.11、0.12、0.15、0.49,则这些字符所对应的哈夫曼编码可能为( )。

A.
`0, 110, 111, 1110, 1011, 11`
B.
`1, 0, 10, 11, 1, 1`
C.
`0001, 0000, 010, 011, 001, 1`
D.
`0, 0110, 111, 1110, 1011, 011`

正确答案C

解析详情
答案 C哈夫曼编码
思路与推导
  1. 依次合并最小权值:0.04+0.09=0.130.04+0.09=0.13,0.11+0.12=0.230.11+0.12=0.23,0.13+0.15=0.280.13+0.15=0.28,再合并 0.230.23 与 0.280.28。

  2. 因此频率 0.490.49 的 F 可用 1 位码,A、B 最深;C 中码长与该树一致且所有码互为非前缀。

第 9 题(单选题,2 分)

下列选项中( )是操作系统的名字。

A.
CJYYDS
B.
Safari
C.
Android
D.
WeChat

正确答案C

解析详情
答案 C系统软件
思路与推导
  1. Android 负责管理移动设备的硬件资源并为应用提供运行环境,属于操作系统;Safari 和 WeChat 都是运行在操作系统上的应用软件。

第 10 题(单选题,2 分)

无向图 G=(V,E)G=(V,E),其中 V={a,b,c,d,e,f,g}V=\{a,b,c,d,e,f,g\},E={(a,b),(a,c),(b,d),(c,d),(d,e),(d,f),(e,g),(f,g)}E=\{(a,b),(a,c),(b,d),(c,d),(d,e),(d,f),(e,g),(f,g)\}。从顶点 a 开始进行深度优先搜索,遍历结果不可能为( )。

A.
`acdegfb`
B.
`abdcfge`
C.
`abcdefg`
D.
`abdfgec`

正确答案C

解析详情
答案 C深度优先搜索
思路与推导
  1. 若从 a 先访问 b,则 b 唯一尚未访问的邻点是 d,DFS 必须继续到 d,不能马上回到 a 再访问 c。

  2. 序列 abcdefg 的前三个顶点 a、b、c 因而不可能连续出现。

第 11 题(单选题,2 分)

给定二叉树的前序遍历序列为 `[a,b,c,d,f,g,h,i,e]`,中序遍历序列为 `[b,a,f,d,h,g,i,c,e]`,该二叉树的后序遍历结果是( )。

A.
`[b,f,h,i,g,d,e,c,a]`
B.
`[b,f,g,h,i,d,e,c,a]`
C.
`[f,h,i,g,d,e,c,b,a]`
D.
`[f,h,i,g,d,c,b,e,a]`

正确答案A

解析详情
答案 A二叉树遍历还原
思路与推导
  1. 前序首项 a 为根,中序将其分成左子树 b 和右子树 f,d,h,g,i,c,e;递归划分后右子树根为 c,其左根为 d、右子为 e。

  2. 按“左子树—右子树—根”得到后序序列 b,f,h,i,g,d,e,c,a。

第 12 题(单选题,2 分)

对整数序列 `5,17,28,96,13,15` 排序。第一次后为 `5,17,28,13,15,96`,第二次后为 `5,17,13,15,28,96`,第三次后为 `5,13,15,17,28,96`。使用的排序算法可能是( )。

A.
冒泡排序
B.
希尔排序
C.
归并排序
D.
计数排序

正确答案A

解析详情
答案 A冒泡排序
思路与推导
  1. 每一趟都把当前未排序部分的最大值向右交换到末端:第一趟固定 96,第二趟固定 28,第三趟固定 17。

  2. 这正是从左到右进行冒泡排序的中间状态。

第 13 题(单选题,2 分)

以下算法描述正确的是( )。

A.
算法的时间复杂度是指执行算法程序所需要的时间
B.
算法必须能在执行有限个步骤之后结束
C.
算法必须用高级语言描述
D.
算法的空间复杂度是指算法程序中指令(或语句)的条数

正确答案B

解析详情
答案 B算法的基本特性
思路与推导
  1. 算法具有有穷性,必须在执行有限个步骤后结束。时间复杂度描述基本操作次数随输入规模的增长关系,并非一次运行的实际耗时。

第 14 题(单选题,2 分)

小明的团队中只有 10 个成员(算上小明),他讨厌其中的两个人,又喜欢其中的三个人。他可以带任意多个成员出差,也可以自己一个人去。他不想和讨厌的人出差,也不想让出差的人中一个自己喜欢的都没有。他一共有( )种出差人选方案。

A.
113
B.
105
C.
112
D.
104

正确答案C

解析详情
答案 C乘法原理与容斥
思路与推导
  1. 排除 2 个讨厌的人后,可选者为 3 个喜欢的人和 4 个其他成员;喜欢的人至少选 1 个,有 23−1=72^3-1=7 种。

  2. 其他 4 人任意选择,共 (23−1)×24=112(2^3-1)\times2^4=112 种方案。

第 15 题(单选题,2 分)

一个递增的 `int` 类型数组 `n` 有 200 个元素,如果用二分法查找数组中的整数 `num`,最多需要比较( )次就可以断定该 `num` 是否在数组 `n` 中。

A.
5
B.
6
C.
7
D.
8

正确答案D

解析详情
答案 D二分查找复杂度
思路与推导
  1. 最坏情况下每次比较后搜索区间约减半;27=128<200≤256=282^7=128<200\le 256=2^8,因此最多需要 8 次比较。

二、阅读程序(1)

1 | #include <iostream>
2 | using namespace std;
3 |
4 | bool isPrime(int n) {
5 |     for (int i = 2; i < n; ++i)
6 |         if (n % i == 0) return false;
7 |     return true;
8 | }
9 | int Euler(int n) {
10 |     int ret = n;
11 |     for (int i = 2; i <= n; ++i)
12 |         if (n % i == 0 && isPrime(i))
13 |             ret = ret / i * (i - 1);
14 |     return ret;
15 | }
16 |
17 | int main() {
18 |     int x;
19 |     cin >> x; // 保证 x 是正整数且不大于 100000000
20 |     cout << Euler(x) << endl;
21 |     return 0;
22 | }

第 16 题(判断题,2 分)

当输入为 12 时,程序的输出为 4。( )

正确答案正确

解析详情
答案 正确欧拉函数与质因数分解
思路与推导
  1. 12=22×312=2^2\times3,ret 初值为 12;处理质因数 2 后变为 12/2×1=612/2\times1=6,处理质因数 3 后变为 6/3×2=46/3\times2=4。

  2. 因此输出 4。

第 17 题(判断题,2 分)

将 `isPrime()` 函数中的 `for` 循环改成 `for (int i = 2; i * i < n; ++i)`,程序结果不变。( )

正确答案错误

解析详情
答案 错误素数判定边界
思路与推导
  1. 条件 i*i<n 会漏检平方根本身,例如 n=4 时循环一次也不执行,从而把 4 误判为素数。

  2. Euler(4) 还会错误地再次处理因数 4,结果由 2 变成 0。

第 18 题(判断题,2 分)

将 `ret = ret / i * (i - 1);` 改成 `ret = ret * (i - 1) / i;`,结果可能与原程序结果不一样。( )

正确答案正确

解析详情
答案 正确整数运算顺序与溢出
思路与推导
  1. 数学上两式等价,但 int 先乘可能溢出。例如 n=65537 时,原式先除得到 65536;改式先算 65537×6553665537\times65536,已超过 32 位有符号整数范围。

  2. 因此程序结果可能不同。

第 19 题(单选题,3 分)

将 `Euler()` 函数中的 `for` 循环改成 `for (int i = 2; i * 2 < n; ++i)`,下列哪一个输入的结果会与原程序结果不同?( )

A.
25
B.
26
C.
27
D.
28

正确答案B

解析详情
答案 B质因数枚举边界
思路与推导
  1. 输入 26 时质因数为 2 和 13;新条件 i*2<26 会在 i=13 处失败,漏掉质因数 13。

  2. 原程序得到 26×(1−1/2)×(1−1/13)=1226\times(1-1/2)\times(1-1/13)=12,修改后只处理 2,得到 13。

第 20 题(单选题,3 分)

当输入为 2025 时,程序的输出为( )。

A.
135
B.
270
C.
540
D.
1080

正确答案D

解析详情
答案 D欧拉函数计算
思路与推导
  1. 2025=34×522025=3^4\times5^2,其不同质因数只有 3 和 5。

  2. 程序依次更新 ret,得到 2025×(1−1/3)×(1−1/5)=10802025\times(1-1/3)\times(1-1/5)=1080。

二、阅读程序(2)

1 | #include <iostream>
2 | #include <vector>
3 | using namespace std;
4 | int Deal(int n, int s, vector<int>& arr) {
5 |     int count = 0;
6 |     vector<int> sum;
7 |     sum.push_back(0);
8 |     for (int i = 1; i <= n; ++i) {
9 |         sum.push_back(sum[i - 1] + arr[i]);
10 |     }
11 |     for (int i = 1; i <= n; ++i) {
12 |         int L = i, R = n;
13 |         while (L <= R) {
14 |             int mid = (L + R) >> 1;
15 |             if (sum[mid] - sum[i - 1] <= s) {
16 |                 L = mid + 1;
17 |             } else {
18 |                 R = mid - 1;
19 |             }
20 |         }
21 |         count += (R - i + 1);
22 |     }
23 |     return count;
24 | }
25 | int main() { // 输入均为正整数
26 |     int n, s;
27 |     cin >> n >> s;
28 |     vector<int> arr(n + 1);
29 |     arr[0] = 0;
30 |     for (int i = 1; i <= n; ++i) cin >> arr[i];
31 |     cout << Deal(n, s, arr) << endl;
32 |     return 0;
33 | }

第 21 题(判断题,2 分)

输入如下时,输出为 8。( )

4 5
1 2 3 4

正确答案错误

解析详情
答案 错误前缀和与连续子数组计数
思路与推导
  1. 和不超过 5 的连续子数组分别为 [1]、[1,2]、[2]、[2,3]、[3]、[4],共 6 个。

  2. 程序对每个起点二分最远合法终点,所以输出 6 而不是 8。

第 22 题(判断题,2 分)

`sum[mid] - sum[i - 1]` 的意思为 `arr[i - 1] + arr[i] + ... + arr[mid]`。( )

正确答案错误

解析详情
答案 错误前缀和区间求和
思路与推导
  1. sum[k] 保存 arr[1] 到 arr[k] 的和,因此 sum[mid]-sum[i-1] 恰好是 arr[i]+arr[i+1]+...+arr[mid]。

  2. 表达式中不包含 arr[i-1]。

第 23 题(判断题,2 分)

若输入完对 `arr` 数组排序,结果与原程序结果一样。( )

正确答案错误

解析详情
答案 错误连续子数组与元素顺序
思路与推导
  1. 排序会改变哪些元素彼此连续,结果可能变化。例如 arr=[1,4,1]、s=2 时原序列有 2 个合法子数组,排序成 [1,1,4] 后有 3 个。

第 24 题(单选题,2 分)

若将主函数里的 `arr[0] = 0` 改为 `arr[0] = 1`,结果与改之前相比( )。

A.
只可能变大
B.
只可能变小
C.
一定不变
D.
可能变大也可能变小

正确答案C

解析详情
答案 C数组下标与数据流
思路与推导
  1. Deal 中构造前缀和时只读取 arr[1] 到 arr[n],sum[0] 则由 sum.push_back(0) 单独确定。

  2. arr[0] 从未参与任何后续计算,所以改为 1 后结果一定不变。

第 25 题(单选题,3 分)

若将 `Deal()` 函数开始的 `sum.push_back(0);` 改为 `sum.push_back(1);`,结果与改之前相比( )。

A.
只可能变大
B.
只可能变小
C.
一定不变
D.
可能变大也可能变小

正确答案C

解析详情
答案 C前缀和差分的不变性
思路与推导
  1. 把 sum[0] 从 0 改为 1 后,递推会使所有 sum[k] 都整体增加 1。

  2. 区间和仍为 (sum[mid]+1)−(sum[i−1]+1)(sum[mid]+1)-(sum[i-1]+1),常数相消,因此所有二分判断和最终计数不变。

第 26 题(单选题,3 分)

若输入的 ss 满足 s>arr[1]+arr[2]+⋯+arr[n]s>arr[1]+arr[2]+\cdots+arr[n],则输出必为( )。

A.
n(n+1)/2n(n+1)/2
B.
n(n−1)/2n(n-1)/2
C.
n(n+1)n(n+1)
D.
n(n−1)n(n-1)

正确答案A

解析详情
答案 A连续子数组数量
思路与推导
  1. 数组元素均为正且 s 大于全数组之和,所以任意连续子数组的和都小于 s,全部会被计数。

  2. 长度为 n 的数组共有 n+(n−1)+⋯+1=n(n+1)/2n+(n-1)+\cdots+1=n(n+1)/2 个连续子数组。

二、阅读程序(3)

1 | #include <iostream>
2 | using namespace std;
3 | int Function(int a, int b) {
4 |     if (b == 0) return 1;
5 |     if (a - 1 >= b) return Function(a - 1, b) + Function(a - 1, b - 1);
6 |     return Function(a - 1, b - 1);
7 | }
8 | int main() { // 输入均为正整数
9 |     int a, b;
10 |     cin >> a >> b;
11 |     cout << Function(a, b) << endl;
12 |     return 0;
13 | }

第 27 题(判断题,2 分)

输入 `5 2` 时,程序的输出为 10。( )

正确答案正确

解析详情
答案 正确组合数递推
思路与推导
  1. 递归满足 F(a,b)=F(a−1,b)+F(a−1,b−1)F(a,b)=F(a-1,b)+F(a-1,b-1),边界 F(a,0)=1,对应组合数 (ab)\binom{a}{b}。

  2. 所以 F(5,2)=(52)=10\binom{5}{2}=10。

第 28 题(判断题,2 分)

输入的 aa 小于 bb 时,`Function` 函数可能陷入无限递归。( )

正确答案错误

解析详情
答案 错误递归终止条件
思路与推导
  1. 当 a<b 时只会执行 F(a-1,b-1),每次都使 b 减 1;经过原 b 次调用后 b=0,触发返回 1。

  2. 因此即使 a 变为非正数,递归仍会在有限步内结束。

第 29 题(判断题,2 分)

程序的运行时间与 bb 相关但与 aa 无关,且 bb 越大运行时间越长。( )

正确答案错误

解析详情
答案 错误递归时间复杂度
思路与推导
  1. 递归树是否分叉由 a-1>=b 决定:固定 b=1 时,增大 a 会增加递归层数;固定 a 时,b=a 只形成单支,而 b 接近 a/2 会产生大量双分支。

  2. 所以运行时间同时与 a、b 有关,且 b 越大并不保证运行越慢。

第 30 题(单选题,2 分)

输入 `10 7` 时,程序的输出为( )。

A.
70
B.
120
C.
360
D.
720

正确答案B

解析详情
答案 B组合数递推
思路与推导
  1. Function(a,b) 计算 (ab)\binom{a}{b},因此 Function(10,7)=(107)=(103)\binom{10}{7}=\binom{10}{3}。

  2. 计算得 10×9×8/(3×2×1)=12010\times9\times8/(3\times2\times1)=120。

第 31 题(单选题,3 分)

若输入的 aa 不变,bb 依次取 1,2,…,a1,2,\ldots,a,则输出结果( )。

A.
单调变大
B.
单调变小
C.
先变小再变大
D.
先变大再变小

正确答案D

解析详情
答案 D组合数的单峰性
思路与推导
  1. 输出依次为 (a1),(a2),…,(aa)\binom{a}{1},\binom{a}{2},\ldots,\binom{a}{a};相邻比值为 (a−b)/(b+1)(a-b)/(b+1)。

  2. 在 b 接近 a/2 前比值不小于 1,越过中点后不大于 1,所以序列先变大再变小。

第 32 题(单选题,3 分)

若将 `if (b == 0)` 改成 `if (b == 1)`,并输入 `12 5`,则程序的输出为( )。

A.
165
B.
330
C.
495
D.
792

正确答案B

解析详情
答案 B递归边界改变
思路与推导
  1. 把边界改为 F(a,1)=1 后,递推结果变为 F(a,b)=(a−1b−1)F(a,b)=\binom{a-1}{b-1}。

  2. 因此 F(12,5)=(114)=330\binom{11}{4}=330。

三、完善程序(1)朴素字符串匹配

(朴素字符串匹配)给定两个非空字符串 aa 和 bb,求 aa 中有多少个与 bb 相同的连续子串。保证 b.length()≤a.length()b.length()\le a.length()。

1 | #include <iostream>
2 | #include <string>
3 | using namespace std;
4 | int main() {
5 |     int count = ①;
6 |     string a, b;
7 |     cin >> a >> b;
8 |     for (int i = 0; i + b.length() <= a.length(); ++i) {
9 |         int flag = ②;
10 |         for (int j = 0; j < ③; ++j) {
11 |             if (④) {
12 |                 flag = 1;
13 |                 break;
14 |             }
15 |         }
16 |         if (flag == 0) ⑤;
17 |     }
18 |     cout << count << endl;
19 |     return 0;
20 | }

第 33 题(单选题,3 分)

① 处应填( )。

A.
0
B.
1
C.
2
D.
3

正确答案A

解析详情
答案 A计数器初始化
思路与推导
  1. count 表示已经找到的匹配子串数量;扫描开始前尚未发现任何匹配,因此必须初始化为 0。

  2. 之后每找到一次完整匹配再执行 ++count。

第 34 题(单选题,3 分)

② 处应填( )。

A.
0
B.
1
C.
2
D.
3

正确答案A

解析详情
答案 A匹配状态初始化
思路与推导
  1. flag=0 表示当前起点 i 尚未发现不相等字符;一旦 a[i+j]!=b[j],才把 flag 置为 1 并退出内层循环。

  2. 因此每次尝试新起点时都要从 0 开始。

第 35 题(单选题,3 分)

③ 处应填( )。

A.
a.length()
B.
b.length()
C.
a.length() - 1
D.
b.length() - 1

正确答案B

解析详情
答案 B朴素字符串匹配
思路与推导
  1. 要判断从 a[i] 开始的子串是否等于 b,必须逐一比较 b 的全部字符,所以 j 的范围是 0 到 b.length()-1。

  2. 循环上界应写 b.length()。

第 36 题(单选题,3 分)

④ 处应填( )。

A.
a[j] != b[j]
B.
a[i + j] != b[i + j]
C.
a[j] != b[i + j]
D.
a[i + j] != b[j]

正确答案D

解析详情
答案 D字符串下标映射
思路与推导
  1. 从主串起点 `i` 开始匹配时,模式串下标 `j` 对应主串下标 `i+j`,因此失配条件应为 `a[i+j] != b[j]`。外层循环已保证这一位置不会越界。

第 37 题(单选题,3 分)

⑤ 处应填( )。

A.
break
B.
continue
C.
count = 1
D.
++count

正确答案D

解析详情
答案 D匹配结果计数
思路与推导
  1. 内层循环结束后 flag==0 表示 b 的所有字符均与 a 从 i 开始的对应字符相同,找到一个匹配子串。

  2. 此时应执行 ++count,把匹配数增加 1。

三、完善程序(2)以最小代价删除字符

给定两个长度相同、仅含小写英文字母的字符串 s1s1 和 s2s2。删除字母 `a`~`z` 的代价依次为 0~25,两个字符串都可以删除字符,求使它们相同的最小代价。

1 | #include <iostream>
2 | #include <vector>
3 | #include <algorithm>
4 | #include <cstring>
5 | using namespace std;
6 | int main() {
7 |     string s1, s2;
8 |     cin >> s1 >> s2;
9 |     int n = s1.length();
10 |     vector<vector<int>> dp(n + 1, vector<int>(n + 1));
11 |     for (int i = 1; i <= n; ++i) {
12 |         for (int j = 1; j <= n; ++j) {
13 |             dp[i][j] = ①;
14 |             if (②) {
15 |                 dp[i][j] = max(dp[i][j], ③);
16 |             }
17 |         }
18 |     }
19 |     int count = 0;
20 |     for (int i = 0; i < n; ++i) count += ④;
21 |     cout << ⑤ << endl;
22 |     return 0;
23 | }

第 38 题(单选题,3 分)

① 处应填( )。

A.
max(dp[i][j], dp[i - 1][j - 1])
B.
max(dp[i - 1][j], dp[i][j - 1])
C.
min(dp[i][j], dp[i - 1][j - 1])
D.
min(dp[i - 1][j], dp[i][j - 1])

正确答案B

解析详情
答案 B加权最长公共子序列动态规划
思路与推导
  1. dp[i][j] 表示两个前缀可共同保留字符所节省的最大删除代价;若暂不匹配末尾字符,就从跳过 s1[i-1] 或跳过 s2[j-1] 中取较大值。

  2. 因此基础转移是 max(dp[i-1][j],dp[i][j-1])。

第 39 题(单选题,3 分)

② 处应填( )。

A.
s1[i] == s2[j]
B.
s1[i - 1] == s2[j - 1]
C.
s1[i] == s2[j - 1]
D.
s1[i - 1] == s2[j]

正确答案B

解析详情
答案 B动态规划下标对应
思路与推导
  1. dp 的 i、j 表示前缀长度,而 C++ 字符串从下标 0 开始,所以这两个前缀的末字符分别是 s1[i-1] 和 s2[j-1]。

  2. 只有二者相等时才能把该字符共同保留。

第 40 题(单选题,3 分)

③ 处应填( )。

A.
dp[i - 1][j - 1] + s1[i - 1] - 'a'
B.
dp[i - 1][j] + s1[i - 1] - 'a'
C.
dp[i][j - 1] + s2[i] - 'a'
D.
dp[i - 1][j - 1] + 2 * (s1[i - 1] - 'a')

正确答案D

解析详情
答案 D加权公共子序列转移
思路与推导
  1. 相等字符共同保留后,两个字符串都无需删除该字符;字符 s1[i-1] 的单次删除代价为 s1[i-1]-'a',总共节省其 2 倍。

  2. 故转移为 dp[i-1][j-1]+2*(s1[i-1]-'a')。

第 41 题(单选题,3 分)

④ 处应填( )。

A.
s1[i] - 'a'
B.
s2[i] - 'a'
C.
s1[i] + s2[i] - 'a'
D.
s1[i] + s2[i] - 2 * 'a'

正确答案D

解析详情
答案 D字符权值累计
思路与推导
  1. count 先累计若删除两个字符串全部字符所需的总代价;位置 i 的贡献为 (s1[i]-'a')+(s2[i]-'a')。

  2. 合并后即 s1[i]+s2[i]-2*'a'。

第 42 题(单选题,3 分)

⑤ 处应填( )。

A.
dp[n][n]
B.
count + dp[n][n]
C.
count - dp[n][n]
D.
count - 2 * dp[n][n]

正确答案C

解析详情
答案 C最小删除代价与最大保留收益
思路与推导
  1. count 是删除全部字符的总代价,dp[n][n] 是选择最优公共子序列后可节省的总代价。

  2. 因此最小实际删除代价为 count-dp[n][n]。