夜雨聆风学习资料网

ARTICLE · 1029633

每周一套 GESP | C++ 三级真题精讲(2026年9月·第15次认证)

每周一套 GESP | C++ 三级真题精讲(2026年9月·第15次认证)

算法与证书之间 | 每周一套 GESP

GESP C++ 三级 真题精讲

2026 年 9 月 · 第 15 次认证

跟着真题学编程,稳步拿下考级

📖 写在前面

小朋友们好!这是 2026 年 9 月的 GESP C++ 三级真题,也是我们专栏第一次精讲三级卷子。三级和一、二级最大的不同是:这是一套几乎全在考"位运算"的卷子——二进制、原码反码补码、与或非、异或、移位、位掩码……选择题和判断题里一大半都和 0、1 有关。

别被"位运算"三个字吓到,它其实特别好玩:你可以把一个整数想象成一排开关,每个开关只有"开(1)"和"关(0)"两种状态,而 & | ^ ~ << >> 就是在批量拨这些开关。把这套开关想明白了,三级就拿下一大半了。

编程题两道:T1《公共二进制位》考按位与 + 统计 1 的个数,T2《分割字符串》考字符串处理,都是三级的经典套路,代码不长,跟紧思路一定能学会。

💡 学习建议:位运算题最好拿一张纸,把每个数写成二进制,一位一位对着算。解析里我会把关键的二进制都列出来,你跟着画一遍,比看十遍文字都管用!

🧰 开考前先复习:六个位运算符

& 按位与:两位都为 1 结果才是 1(像"而且")| 按位或:有一个为 1 结果就是 1(像"或者")^ 按位异或:两位不同为 1、相同为 0(不一样才通电)~ 按位取反:0 变 1、1 变 0(一键全翻转)<< 左移:所有位向左挪,右边补 0,挪一位≈乘 2>> 右移:所有位向右挪,正数挪一位≈除以 2 取整

优先级(高→低):~(取反) > << 、>>(移位) > &(与) > ^(异或) > |(或)。记不住没关系,不确定就加括号,计算机一定按括号算!

📝 单选题精讲(每题 2 分,共 30 分)

1

关于计算机的数据编码,下面说法不正确的是( )。

A. 数字 5,不管采用原码、反码、补码,它的 8 位二进制形式都是 0000 0101

B. 计算机科学中,单位换算默认 1 KB = 1024 B,而非 1000 B

C. 原码是最直观的一种有符号数表示方法

D. 反码运算中,最高位进位要抛掉 ✓

答案:D

解析:A 对:正数的原码、反码、补码三码合一,5 都是 0000 0101。B 对:计算机里 1 KB = 1024 B(2 的 10 次方)。C 对:原码就是"最高位当符号、后面照抄数值",确实最直观。D 错,所以选 D。正确规则是——补码运算时最高位的进位要丢弃;而反码运算时,最高位产生的进位不能扔,要把它"循环加回最低位"(这叫循环进位)。两种码的处理方式正好相反,别记混啦。

📌 补码的进位"扔掉",反码的进位"加回最低位"。

2

关于进制转换,下面说法不正确的是( )。

A. 数字 1101B = 1×2³+1×2²+0×2¹+1×2⁰ = 8+4+0+1 = 13

B. 数字 42Q = 4×8¹+2×8⁰ = 32+2 = 34

C. 数字 2BEH = 2×16²+11(B)×16¹+14(E)×16⁰ = 512+176+14 = 702

D. 345Q = 0E3H(通常写作 E3H) ✓

答案:D

解析:后缀 B 表示二进制、Q(或 O)表示八进制、H 表示十六进制。A、B、C 三个选项按权展开的结果都正确。D 错,所以选 D。345Q = 3×64 + 4×8 + 5 = 192+32+5 = 229;而 E3H = 14×16+3 = 227,两者不相等。和 229 相等的十六进制其实是 E5H(14×16+5=229)。出题人把八进制 5 错当成了十六进制 3。

💡 同一数字可用多种进制表示,换算到十进制一比就知道等不等。

3

下面程序的运行结果是( )。

#include <iostream>

using namespace std;

int main() {

    int x = 1;

    for (int i = 0; i = 0; i <= 5) {

        x++;

    }

    cout << x << endl;

    return 0;

}

A. 1 ✓

B. 10

C. 15

D. 程序编译错误

答案:A

解析:关键看 for 循环括号里的三个表达式,正确写法应是 "初始化; 循环条件; 增量",可这里中间写的是 i = 0赋值号 =),不是比较号 i == 0。它的执行过程是:先 int i = 0 初始化;然后求中间 i = 0,把 0 赋给 i,整个表达式的值就是 0,0 代表"假",循环条件一开始就不成立,循环体一次都不执行。所以 x 始终是初始化的 1,输出 1。

⚠️ 程序没有语法错误,能编译能运行,只是循环不进;i++ 永远执行不到,也不会死循环。

📌 一个等号 = 是"赋值",两个等号 == 才是"判断是否相等",这是考试最爱挖的坑。

4

关于计算机编码中的补码,下面说法错误的是( )。

A. 正数的补码:与其原码、反码相同

B. 负数的补码:将其对应正数的原码按位取反(得到反码),然后加 1

C. 8 位的补码,最大表示数据是 128,最小表示数据是 -128 ✓

D. 负数的补码,从右往左扫描正数的二进制形式,遇到第一个 1 之后,左边的所有位都取反

答案:C

解析:A、B 都是补码的标准定义,正确。D 是求负数补码的一个快捷口诀,也正确。C 错,所以选 C。8 位补码的范围是 -128 ~ +127,不是"最大 128"。因为最高位要留作符号位,正数最大只能到 0111 1111 = +127;而负数方向借助补码可以多表示一个 -128(1000 0000)。

📌 n 位补码范围:-2 的 (n-1) 次方 ~ +2 的 (n-1) 次方 -1。8 位就是 -128~127。

5

下面程序执行的结果是( )。

#include <iostream>

using namespace std;

int main() {

    int x = 2026;

    int count = 0;

    while (x > 0) {

        count++;

        x = x & (x - 1);

    }

    cout << count << endl;

}

A. 2026

B. 8 ✓

C. 16

D. 32

答案:B

解析:这句 x = x & (x - 1) 是位运算里最经典的一招,作用是:把 x 最右边的那个 1 抹掉。每执行一次就少一个 1,count 加一次,所以最终 count 就是 2026 的二进制里 1 的个数。2026 转二进制:2026 = 1024+512+256+128+64+32+8+2,写成二进制是 11111101010,数一数 1 一共有 8 个(我用编译器验证过,输出确实是 8)。

📌 x & (x-1) = 消掉最右边的 1;循环到 0 的次数 = 二进制中 1 的个数。这个套路务必背下来!

6

关于位运算,下列说法错误的是( )。

A. 找唯一数:数组中唯一出现一次、其余出现两次的数,全部异或的结果即为该数(如 [5,7,9,7,5] 异或得 9)

B. 若左移导致高位溢出(超过整型范围),结果不再符合乘法规律

C. 有符号整数左移溢出时结果确定 ✓

D. n+2 位二进制数 A₁A₂…Aₙ00,一定是 4 的倍数(最后两位 00)

答案:C

解析:A 对:异或满足"自己异或自己 = 0、x 异或 0 = x、可交换可结合",成对的数两两抵消,只剩唯一的那个 9。B 对:一旦左移把高位挤出了整型范围(溢出),就不能再简单看成乘 2 了。D 对:二进制末两位是 00,说明它能被 4 整除。C 错,所以选 C。有符号整数一旦左移溢出,就进入了"未定义/实现相关"的行为,结果并不确定,不能拿来做可靠计算。

💡 异或找唯一数是高频考点:成对抵消、孤家寡人留下来。

7

计算 3 & 7 | 15 ^ ~11(a=3, b=7, c=15, d=11)的结果是( )。

A. -9

B. -1

C. -7

D. -5 ✓

答案:D

解析:按优先级先算 ~,再算 &,然后 ^,最后 |。① 3 & 7:0011 & 0111 = 0011 = 3;② ~11:11 = 0000 1011,取反后是 …1111 0100,即 -12;③ 15 ^ (-12):01111 异或 …10100 = …11011,即 -5;④ 3 | (-5):00011 或 …11011 = …11011 = -5。我用编译器实跑 3&7|15^~11,输出就是 -5,选 D。

⚠️ 含 ~ 的位运算结果常是负数,别怕,负数在计算机里用补码表示,按位照算即可。

8

下面是一个加密解密的函数,横线处应该填入的程序段是( )。

#include <iostream>

#include <string>

using namespace std;

int main() {

    int key; cin >> key;

    string msg = "Hello GESP";

    cout << msg << endl;            // 原文

    for (int i = 0; i < msg.size(); i++) {

        msg[i] = msg[i] ^ key;      // 加密

    }

    cout << msg << endl;            // 密文

    __________(横线处)__________

    cout << msg << endl;            // 又变回原文

    return 0;

}

A. 循环执行 msg[i] = msg[i] | key;

B. 循环执行 msg[i] = msg[i] >> key;

C. 循环执行 msg[i] = msg[i] ^ key; ✓

D. 循环执行 msg[i] = msg[i] & key;

答案:C

解析:加密用的是异或:msg[i] ^ key。异或有一个神奇的性质——同一个数异或两次会变回原样:(x ^ key) ^ key = x。所以解密只要把加密的动作原封不动再做一遍就行,选 C。而 | 、>> 、& 都不可逆,没法把密文还原成原文。

🌰 异或加密是真实世界里最简单的加密:加密、解密用同一把钥匙、同一个操作。

🌰 灯泡比喻:按一下开关灯变状态(加密),再按同一个开关灯回到原来(解密)。

9

下列程序分别判断输入的 N 个数是否"可能"是二进制、八进制、十进制、十六进制,横线处应填入( )。

#include <iostream>

using namespace std;

int main() {

    int n = 0; cin >> n;

    for (int i = 0; i < n; i++) {

        char str[11]; cin >> str;

        char max = '0';

        for (int i = 0; str[i] != '\0'; i++)

            if (str[i] > max) max = str[i];

        __________(横线处)__________

    }

    return 0;

}

A. cout << (max <= '1') << " " << (max <= '7') << " " << (max <= '9') << " " << (max <= 'F') << endl; ✓

B. 用数字 1、7、9、'F' 比较(前三个没加引号)

C. 用数字 1、7、9、F 比较(字符都没加引号)

D. 全部用 >= 反向比较

答案:A

解析:程序先找出字符串里最大的那个字符 max。判断思路:最大字符不超过 1 → 可能是二进制;不超过 7 → 八进制;不超过 9 → 十进制;不超过 F → 十六进制。所以四个方向都应是 max <=,且字符要带单引号,选 A。B、C 错在把字符 '1' 写成了数字 1——字符 '1' 的 ASCII 码是 49,和数字 1 完全不是一回事;F 不加引号更是语法错误。D 方向反了。

⚠️ 字符常量必须加单引号。"字符 0"是 ASCII 48,不是数字 0。

10

位掩码管理权限:四个权限各占一个二进制位,mask 初始为 0。要依次完成:添加读、添加写、清除读(其他位不变)、添加删除,横线处应填( )。

const int READ   = 1 << 0;  // 读,第0位

const int WRITE  = 1 << 1;  // 写,第1位

const int MODIFY = 1 << 2;  // 修改,第2位

const int DELETE_= 1 << 3;  // 删除,第3位

int mask = 0;               // 初始无任何权限

__________(横线处)__________

A. mask |= READ; mask |= WRITE; mask &= ~READ; mask |= DELETE_; ✓

B. mask &= READ; mask &= WRITE; mask |= ~READ; mask &= DELETE_;

C. mask |= READ; mask &= WRITE; mask &= ~READ; mask |= DELETE_;

D. mask |= READ; mask |= WRITE; mask |= ~READ; mask |= DELETE_;

答案:A

解析:记住位掩码两套口诀——加权限用 |(或),清权限用 & 配合 ~(与上取反)。添加读:mask |= READ;添加写:mask |= WRITE;清除读且不动其他位:mask &= ~READ(先把读那一位取反成 0,再与一下,正好把这一位关掉);添加删除:mask |= DELETE_。完全对应 A。C 的第二步 mask &= WRITE 会把除写以外的位全清零;D 的 |= ~READ 会把几乎所有位都打开,都不对。

📌 开灯用 |,关灯用 &= ~。这是位掩码的灵魂操作。

11

下面选项中提到的变量都是正整数,关于位运算,下面说法错误的是( )。

A. num >> 1 对于正整数,等价于对 num 做整数除以 2、向下取整

B. num | 0x0F 可以把 num 的低 4 位全部置为 1,高位保持不变

C. num ^ num 的结果一定等于 0

D. 若 num 右移时低位被移出丢弃,结果一定符合除法的数学规律 ✓

答案:D

解析:A、B、C 都正确。D 用了"一定"这种绝对化说法,正是出题人埋的雷:右移是"向下取整"的除法,并不等于数学上精确的除法结果。例如 7 >> 1 = 3,而 7 ÷ 2 = 3.5,奇数右移会"丢掉"小数部分,所以"一定符合除法的数学规律"站不住脚,D 的说法错误,选 D。⚠️ 左移≈乘2、右移≈除2 的口诀,安全前提是"正数且不溢出"。
12

已知 a=0, b=2, c=11, d=7, e=5,表达式 !a & b << c ^ d | e 的结果是( )。

A. 5

B. 13

C. 7 ✓

D. 28

答案:C

解析:按优先级:! 最高,接着 << ,然后 & 、^ 、|。① !a:a=0,!0 = 1;② b << c:2 << 11 = 4096;③ 1 & 4096:4096 的最低位是 0,所以 = 0;④ 0 ^ 7 = 7;⑤ 7 | 5:0111 | 0101 = 0111 = 7。我用编译器实跑该表达式,输出 7,选 C。

⚠️ !a 先变成 1,而 1 与 4096 相与是 0;这道题的坑全在优先级上,拿不准就加括号。

13

关于 string 的成员函数,下面说法错误的是( )。

A. size() 返回字符个数、不含末尾 '\0';length() 和 size() 完全等价

B. s.empty():字符串为空返回 true,否则返回 false

C. s.substr(pos, len) 省略第二个参数时,从 pos 一直截取到末尾

D. s.erase(2) 表示删除字符串中下标为 2 的这一个字符 ✓

答案:D

解析:A、B、C 都是 string 成员函数的正确用法。D 错,所以选 D。s.erase(2) 只传一个位置参数时,含义是从下标 2 开始,一直删除到字符串末尾,删掉的是"后面一整段",不是只删第 2 个字符。若只想删下标 2 这一个字符,要写 s.erase(2, 1)(第二个参数 1 表示删除长度为 1)。

⚠️ erase(pos) 删到结尾,erase(pos, len) 才是删 len 个字符,务必分清。

14

以下数组定义,符合 C++ 语法的是( )。

A. int a[] = {1, 2, 3.0};

B. int b['A']; ✓

C. int c[ ];

D. float d[10.0];

答案:B

解析:int b['A']; 中,'A' 是字符常量,它的 ASCII 码是整数 65,数组大小相当于 65,是一个合法的整型常量,所以 B 正确。A 错:int 数组里给了 3.0 浮点初值,类型不符;C 错:声明数组不给大小、也不初始化,编译器不知道开多大空间;D 错:数组大小必须是整数,10.0 是浮点数。

📌 数组大小必须是"整型常量表达式",字符常量本质上也是个整数,所以可以。

15

1000 个酒缸恰好 1 缸有毒,毒酒喝下 24 小时后毒发;共有 48 小时,可做两轮实验,每轮能重新安排老鼠喝酒。每只老鼠对一缸酒有三种状态:两轮都不喝、只第一轮喝、只第二轮喝。要保证找出毒酒,最少需要几只老鼠?( )

A. 5

B. 7 ✓

C. 10

D. 9

答案:B

解析:这是"二进制/三进制找毒酒"的经典变形。关键是:每只老鼠对每一缸酒有 3 种可区分的结果——两轮都不喝、第一轮喝、第二轮喝(因为有两轮,能看出毒是在第几轮发作)。于是 n 只老鼠一共能编码 3ⁿ 种情况。要分辨 1000 缸,需要 3ⁿ ≥ 1000:3⁶ = 729 不够,3⁷ = 2187 够了,所以最少 7 只,选 B。

🌰 一只老鼠每轮只提供"喝/不喝",两轮组合出 3 种有效信号,本质是三进制编码。

💡 对比:只有一轮实验时每只老鼠只有 2 种状态,要用 2ⁿ ≥ 缸数(二进制);两轮就是 3ⁿ。

✅ 判断题精讲(每题 2 分,共 20 分)

1

补码做加法运算时,如果最高位产生进位,需要把该进位循环加到结果的最低位。

错误 ×

解析:说反了。补码加法最高位的进位直接丢弃即可,不需要加回最低位;要做"循环进位"(把进位加回最低位)的是反码运算。把单选第 1 题和这题放在一起记:补码扔、反码加回。
2

求 -13 的补码,可以对正数 13 的二进制,从右向左扫描,遇到第一个 1 之后,右边所有位全部取反、左边保持不变。

错误 ×

解析:方向说反了。口诀是:从右往左找到第一个 1,这个 1 和它右边的位保持不变,左边所有位取反。题目却让"右边取反、左边不变",正好颠倒,所以错误。
3

一个 8 位二进制原码表示的有符号数,可表示的数值范围是 -128 到 +127。

错误 ×

解析:-128~+127 是 8 位补码的范围。原码最高位是符号位,8 位原码的范围是 -127~+127,而且原码里 0 有"+0""-0"两种表示,它表示不了 -128。张冠李戴,错误。
4

这段代码执行后,能够将数组中所有大于 0 的元素赋值为 0。

int a[] = {2, -5, 7, 0, -1};

int n = sizeof(a) / sizeof(a[0]);

for (int num : a) {

    if (num > 0) {

        num = 0;

    }

}

错误 ×

解析:问题出在范围 for 的写法 for (int num : a):这里的 num 是数组元素的一份副本(拷贝),函数里把 num 改成 0,改的只是临时副本,原数组一个数都不会变。想真正修改数组,必须用引用:for (int &num : a)。所以说法错误。
5

a 是 int 有符号整型,若 (a & 1) == 1 结果为 true,则 a 一定是正奇数。

错误 ×

解析:a & 1 只看最低位。负数在计算机里用补码表示,负奇数的最低位同样是 1,例如 -1 的补码是全 1,-1 & 1 = 1,结果也为 true,可 -1 是负数。所以只能断定 a 是"奇数",不能断定"正",说法错误。
6

char str[4] = "GESP"; 将在内存空间中给 str[4] 分配 5 个字节。

错误 ×

解析:数组大小已经写死为 4,就只分配 4 个字节。而字符串 "GESP" 含末尾的 \0 实际需要 5 个字节,4 个格子装不下,char str[4] = "GESP" 是非法初始化,直接无法通过编译(编译器报错,而不是运行时越界)。"给 str[4] 分配 5 个字节"的说法自相矛盾、也是错的。正确写法应是 char str[5]。
7

判断字符串中字符是否是 '0' 或者 '1',可以写条件:if(str[i]==0 || str[i]==1)。

错误 ×

解析:又踩了"字符 vs 数字"的坑。字符 '0''1' 的 ASCII 码分别是 48、49;而条件里写的 0、1 是整数。正确写法是 if(str[i]=='0' || str[i]=='1'),字符必须带单引号,错误。
8

下面程序执行后,将输出 98。

#include <iostream>

using namespace std;

int main() {

    int a = 'a', b = 'b';

    if (a > 0 || b++ > 0) { }

    cout << b;

}

正确 √

解析:'a' 的 ASCII 码是 97,'b' 是 98。if 条件用的是 ||(逻辑或),具有'短路'特性:左边 a > 0(97>0)已经为真,右边 b++ 就根本不会执行,b 保持 98 不自增。最后输出 b 仍是 98,说法正确(我用编译器实跑确认输出 98)。

📌 || 左真则右不算,&& 左假则右不算——短路求值是判断题常客。

9

枚举可以在保证不漏解的前提下缩小枚举范围,不是必须遍历全部。

正确 √

解析:正确。枚举的核心是"不遗漏",但完全可以根据条件合理缩小范围,只枚举有希望的部分,从而提高效率。只要能保证答案一定还在缩小后的范围里,就不必把所有情况都跑一遍。
10
流程图:初始化 sum=0, i=0;当 i

错误 ×

解析:循环 i 从 0 到 n-1,累加的是 a[0]、a[1]……a[n-1],一共 n 个元素,并没有包含 a[n]。而题目声称它能求 a[0]~a[n](共 n+1 个元素)的总和,少加了 a[n],所以错误。(若数组本来就只有 a[0]~a[n-1],那流程图是对的,但对照题面说法它漏掉了 a[n]。)

💻 编程题精讲(每题 25 分,共 50 分)

🔢 编程题 T1:公共二进制位(位运算)

题目描述:

小红有 n 个非负整数。把每个数写成二进制后,她想知道:有多少个二进制位,在所有数里都为 1。

二进制位从右向左编号为 0、1、2……;某个数若没有第 k 位(位数不够),就认为它的第 k 位是 0。

输入:第一行整数 n;第二行 n 个非负整数。

输出:一个整数,表示在所有数中都为 1 的二进制位数量。

样例:输入 3 个数 13、7、15,输出 2

13 = 1101 7 = 011115 = 1111

竖着看每一位,第 0 位三个都是 1,第 2 位三个也都是 1,共 2 位,所以答案是 2。

数据范围:2 ≤ n ≤ 20000,0 ≤ aᵢ ≤ 10⁹。

🧠 解题思路:

题目要求"某一位在所有数里都为 1"。回忆 &(按位与)的规则:两位都为 1,结果才是 1。这不正好就是"所有数这一位都为 1"的意思吗?

所以分两步:第一步,把所有数全部按位与起来,得到 S。S 的某一位是 1,当且仅当每个数的这一位都是 1。第二步,数一数 S 的二进制里有几个 1,就是答案。数 1 可以用 S & 1 看最低位、再 S >>= 1 右移。

#include <iostream>

using namespace std;

int a[20010];

int main() {

    int n;

    cin >> n;

    for (int i = 0; i < n; ++i)

        cin >> a[i];

    int S = a[0];                 // 先放第一个数

    for (int i = 1; i < n; ++i)

        S &= a[i];                // 依次把所有数"按位与"起来

    int ans = 0;

    while (S > 0) {

        if (S & 1) ans++;         // 最低位是1,计数+1

        S >>= 1;                  // 右移一位,继续看下一位

    }

    cout << ans;

    return 0;

}

⚠️ 易错点:1. S 的初值要设成第一个数 a[0],不能设成 0(设 0 的话,0 和谁与都是 0)。2. 位数不够的数高位自动算 0,与的时候天然满足题意,不用特殊处理。3. n 最大 2 万、数最大 10⁹,用 int 存得下,整体 O(n),时间足够。

✂️ 编程题 T2:分割字符串(字符串模拟)

题目描述:

小明按越来越"长"的连续空格来切字符串:

① 第一次:找到第 1 个空格,在这儿切开,丢掉这个空格;② 第二次:在剩下的右半段里,找到连续 2 个空格再切,丢掉这 2 个空格;③ 第三次:找连续 3 个空格……以此类推,每次需要的连续空格数加 1,直到再也找不到为止。

依次输出每次切下的左半段,最后再输出剩下的右半段。若一次都切不开,直接输出原字符串。注意:可能切出空字符串(空行也要照常输出,不能省)。

输入:一行字符串(只含大小写字母、数字、空格,且不以空格结尾)。

输出:若干行,依次为每次的左半段,最后一行是剩余的右半段。

样例 1:输入 sdfd 55 55  99312,输出四行:sdfd / 55 / 55 / 99312。

第一次用 1 个空格切下 sdfd;第二次需要连续 2 个空格,"55 55"中间只有 1 个不够,真正的连续空格在后面,切下 "55 55",右侧还多带 1 个空格;这时只剩 1 个空格,凑不齐第 3 次的 3 个,结束,最后输出 99312。

样例 2:输入 AbCd   999(中间 3 个空格),第一次切掉 1 个空格、左半段是 AbCd;剩下串以 2 个空格开头,第二次切出空字符串(要输出一个空行),最后剩 999。

数据范围:字符串长度不超过 1000,仅一行但含若干空格。

🧠 解题思路:

因为输入带空格,普通 cin 读到空格就停,所以必须用 getline(cin, s) 读整行。

用变量 k 表示"第几次分割",第 k 次就拼出 k 个连续空格 spaces,再用 s.find(spaces) 找它第一次出现的位置 pos:· 找不到(返回 -1)就 break;· 找到就输出 pos 左边的字符,然后把"pos+k 之后"的部分拼成新的 s,继续 k+1 次。循环结束后,把最后剩下的 s 输出即可(它天然处理了"一次都切不开"和"切出空行"两种情况)。

#include <iostream>

#include <string>

using namespace std;

int main() {

    string s = "";

    getline(cin, s);              // 必须用getline读整行(含空格)

    for (int k = 1; ; ++k) {      // 第k次分割,需要连续k个空格

        string spaces = "";

        for (int i = 0; i < k; ++i)

            spaces += " ";        // 拼出k个连续空格

        int pos = s.find(spaces); // 在当前串里找这k个空格

        if (pos < 0 || pos >= (int)s.size())

            break;                // 找不到,就不能再分割了

        for (int i = 0; i < pos; ++i)

            cout << s[i];         // 输出分隔符左边

        cout << endl;

        string rest = "";

        for (int i = pos + k; i < (int)s.length(); ++i)

            rest += s[i];         // 跳过这k个空格,保留右边

        s = rest;                 // 右边成为新的字符串继续分割

    }

    cout << s << endl;            // 输出最后剩下的右侧字符串

    return 0;

}

⚠️ 易错点:1. 一定用 getline 读入,cin 读不到空格。2. 每次找的是"连续 k 个空格",用 s.find(空格串) 最方便;切完要跳过这 k 个空格(从 pos+k 开始留右边)。3. 空字符串也要输出,循环里照常 cout 再 endl,就会留下一个空行。4. s.find 找不到返回 -1,这是结束循环的信号。

🎯 考点总结与备考建议

📊 本套考点分布:

• 二进制与编码:原码/反码/补码、范围、进位规则、进制换算(单选 1、2、4,判断 1、2、3)

• 位运算核心:& | ^ ~ << >>、优先级、x&(x-1) 数 1、异或找唯一数/加密、位掩码权限、右移除法(单选 5、6、7、8、10、11、12,判断 5)

• 字符与数组:字符常量与 ASCII、字符/数字区分、数组定义与越界、范围 for 引用(单选 9、14,判断 4、6、7)

• string 与程序阅读:size/empty/substr/erase、for 循环条件、短路求值、流程图(单选 3、13,判断 8、10)

• 数学思维:三进制编码找毒酒(单选 15)、枚举范围(判断 9)

• 编程题:按位与统计公共 1 位、按递增空格数分割字符串

💪 备考建议:

1. 把位运算当成"拨开关":六个运算符亲手在纸上算几十道,尤其练熟 ~、移位后的负数补码。

2. 背下三个高频套路:x&(x-1) 数 1、异或找唯一数/可逆加密、| 加权限 &~ 清权限。

3. 记牢运算符优先级:不确定就加括号,考试不会因为多加括号扣分。

4. 分清字符和数字:'0' 是 48、'A' 是 65,字符常量一定带单引号;字符串含空格用 getline。

5. 读程序题动手跟踪:for 三个表达式、短路 ||&&、范围 for 是否加引用,都要一步一步写下来。

6. 编程题先小后大:用样例在纸上模拟整个切分/与运算过程,再动手写,注意空行、初值和边界。

— END —

算法与证书之间 · 每周一套 GESP · 小昕

真题来源:CCF GESP 2026 年 9 月 C++ 三级认证

相关学习资料

返回首页浏览学习资料