GESP二级编程题解析:字符串、数组、枚举与易错点

发布时间:2026/10/12 14:44:11
GESP二级编程题解析:字符串、数组、枚举与易错点
202603批次的GESP二级出分那天我正好在帮几个考生复盘。过了的普遍觉得“也就那样”没过的多半栽在同一类问题上不是不会写代码而是没读懂题、没处理好边界、或者被输入输出的一个空格卡住整道题。二级这个级别语法点其实就那么多难的是把语法组合起来解决实际的小问题。这篇文章不打算把考纲从头抄一遍直接围绕二级最常考的字符串、数组、枚举三类编程题把题目思路、代码实现、考场操作全部拆开讲清楚。如果你正准备考二级或者刚学完C想找个方向练手跟着这篇的思路走一遍会比闷头刷题高效得多。1. 二级到底在考什么从语法到算法的分水岭1.1 考纲核心与能力要求GESP二级的定位很明确它考的不是“你会不会写代码”而是“你能不能独立完成一个完整的小程序”。一级还在考察变量赋值、顺序结构、分支结构这些最基础的东西到了二级循环、数组、字符串、枚举、模拟这些内容会集中出现难度直接从“照着模板改”跳到了“自己设计思路”。我见过不少考生在一级阶段靠背代码勉强过关到二级就明显吃力原因就在这里二级的选择题会考表达式求值、运算符优先级、循环执行次数、数组越界判断这些光靠记忆是撑不住的必须真正理解程序的执行过程。而编程题更是直接要求输出正确结果任何一点逻辑漏洞都会扣分。从能力要求上看二级考生需要具备这样几个基本素质能准确读题并提取输入输出格式能根据题目描述选择合适的数据结构数组、字符串、简单的结构体能用循环和分支组合出正确的逻辑能检查自己代码的边界情况。说白了这个阶段就是在培养“把一个自然语言描述的问题翻译成C程序”的能力。有同学会问需不需要提前学算法我的建议是二级还不需要碰递归、排序、贪心这些内容枚举和模拟就是最主要的算法思想。把这两样练熟比提前背一堆高级算法更划算。刷题的时候如果总想着用“高级技巧”解决简单题反而容易忽略基础逻辑训练到三级再补反而更困难。1.2 题型结构与分值分布GESP二级的卷面结构和大多数编程等级考试类似由选择题和编程题两大部分组成。不同批次的题量和分值会有微调但整体比例相对稳定基础题型占四成左右编程题占六成左右编程题才是真正的拉分项。以常见的批次为例大致可以这样理解题型大致数量分值特点考查重点单选题约20题每题约2分语法细节、表达式、程序阅读、简单逻辑编程题2-3题按测试点给分字符串处理、数组模拟、枚举求解选择题大多比较直白比如给你一段代码问输出什么、某个表达式的值是多少、循环结束后某个变量的值是什么。这种题最怕粗心运算符优先级和自增自减是高频出错点。编程题则是分测试点给分部分通过也能拿到对应的分数这意味着哪怕你只写出一个暴力解法也能抢救回一部分分完全空题是最亏的。另外要提醒一点GESP的编程题是机器评测输出格式必须严格匹配样例。多一个空格、少一个换行都可能导致整道题零分。202603批次就有一道题很多人逻辑全对结果输出末尾多打了空格白丢了不少分这种细节在后面会有专门的分析。2. 高频编程题拆解三道典型题目的完整复盘这一节我会拿三道有代表性的题目来拆解。它们不是202603批次的原始真题而是根据该批次前后二级命题方向整理的同类题考点和难度都贴近真实考试。你可以把它们当成真题来练做完之后对照我的代码和易错点分析看看差在哪里。2.1 字符串处理单词首字母转大写先看一道经典的字符串处理题这类题几乎每年都会出现因为字符串操作是二级的核心考点之一。题目大意输入一行字符串长度不超过200可能包含多个空格。请把每个单词的首字母改为大写其余字母改为小写非字母字符保持不变。单词定义为连续的非空格字符序列。输入输出样例输入 Hello WORLD cpp 123 输出 Hello World Cpp 123解题思路这道题的关键在于“如何判断一个字母是不是单词的首字母”。很多人第一反应是找到空格然后处理空格后面的字符但这样边界条件很多比如行首、连续多个空格、单词内部有数字等情况都很容易漏。更好的方案是用一个布尔标志位isWordStart来跟踪当前字符是否位于新单词的开头。初始化为true因为行首一定是一个新单词的开始。遍历字符串时遇到空格就把标志位设为true遇到非空格字符就看这个标志位如果是true说明当前字符需要转大写然后立即把标志位设为false如果是false说明当前字符在单词内部需要转小写。参考代码#include iostream #include string using namespace std; int main() { string s; getline(cin, s); bool isWordStart true; for (int i 0; i (int)s.size(); i) { char c s[i]; if (c ) { cout c; isWordStart true; continue; } if (isWordStart) { if (c a c z) { c - 32; } isWordStart false; } else { if (c A c Z) { c 32; } } cout c; } cout endl; return 0; }易错点分析第一读入方式。用cin s读入字符串会在空格处截断只能读到第一个单词后面的内容全丢。整行读入必须用getline(cin, s)。很多翻车案例都是这一行写错。第二状态标志位的更新。isWordStart只能在空格处重置为true遇到非空格字符后要立刻设为false。如果忘了在非空格分支里更新状态那么一个单词里的每个字母都会被当成首字母。第三大小写转换的边界。ASCII码中大写字母 A 到 Z 是 65 到 90小写 a 到 z 是 97 到 122差值正好是 32。小写转大写用c - 32大写转小写用c 32。但注意数字符号这类非字母字符不应该被转换所以转换前要判断字符是否在字母范围内。复杂度和数据范围字符串长度最多200单层循环复杂度O(n)空间O(1)对二级来说非常友好。我上课时常跟学生说字符串题不怕代码长怕的是没搞清楚“当前处于什么状态”用状态标志位往往比死记硬背空格判断要简单得多。2.2 数组模拟报数出圈问题第二类高频考点是数组模拟常见的有报数出圈、排队插队、元素标记等。这类题考察的就是用数组表示现实场景的能力报数出圈是其中的经典代表。题目大意n个人围成一圈编号从1到n。从第1个人开始从1报数报到m的人出圈然后下一个人重新从1开始报数直到所有人都出圈。按出圈顺序输出每个人的编号。数据范围1≤n≤1001≤m≤10。输入输出样例输入 5 3 输出 3 1 5 2 4我建议你先把样例手算一遍从1号开始报数1号报12号报23号报3出圈接着从4号报15号报21号报3出圈然后从2号报14号报25号报3出圈……这个手动模拟的过程能帮你确认自己对题意的理解写代码时才不会跑偏。解题思路用一个bool out[105]数组标记每个人是否已经出圈false表示还在圈内。然后维护三个变量left记录剩余人数idx记录当前扫描到的下标cnt记录当前报数。循环扫描数组如果当前位置的人已经出圈直接跳过如果还在圈内就让cnt加一。当cnt等于m时标记出圈并输出编号cnt重置为0left减一。下标移动用取模运算(idx 1) % n实现循环。参考代码#include iostream using namespace std; int main() { int n, m; cin n m; bool out[105] {false}; int left n; int idx 0; int cnt 0; while (left 0) { if (!out[idx]) { cnt; if (cnt m) { out[idx] true; cout idx 1; if (left 1) cout ; left--; cnt 0; } } idx (idx 1) % n; } cout endl; return 0; }易错点分析第一数组下标的处理。题目中人的编号是1到n但数组下标是0到n-1所以输出时要写idx 1。很多考生习惯性直接输出idx导致答案整体少一。第二取模运算。idx (idx 1) % n让下标在到达数组末尾后重新回到开头模拟“围成一圈”的效果。取模的对象是n不是left因为即便有人出圈总人数n是不变的。第三出圈后cnt的处理。必须在报数达到m时把cnt重置为0而不是重置为1。因为下一次遇到圈内的人时会先自增到1如果重置成1就会多报一位。第四m1的特殊情况。如果m等于1第一个人会立即出圈然后第二个人又立即出圈。上面代码中cnt后等于1正好命中条件所以能正确处理。复杂度方面外层while循环执行n次每次内部扫描最坏要移动n步整体最坏是O(n²)。n最大100完全没问题。m最大只有10也可以优化成直接跳步但二级阶段能写出数组模拟已经很好了没必要过度优化。2.3 枚举求解平方和数对第三类高频考点是枚举二级的枚举题通常不会太复杂重点在于分析枚举范围而不是盲目把整个数据空间都跑一遍。题目大意给定一个正整数nn≤10000输出所有满足 a² b² n 的正整数对(a, b)其中1≤ab。如果有多组按a从小到大输出如果不存在输出None。输入输出样例输入 25 输出 3 4 输入 65 输出 1 8 4 7解题思路最朴素的想法是让a从1枚举到nb从1枚举到n判断平方和是否等于n。但这样做没有必要的浪费。因为a和b都为正数且ab所以a² 和 b² 都不可能超过n也就是说a和b的最大值都在sqrt(n)左右。于是可以这样剪枝外层循环a从1开始条件设为a * a n内层循环b从a1开始条件设为b * b n。这样不仅能保证ab还能大幅减少无效枚举。枚举到一组就输出一组最后用一个布尔变量found记录是否存在答案。参考代码#include iostream using namespace std; int main() { int n; cin n; bool found false; for (int a 1; a * a n; a) { for (int b a 1; b * b n; b) { if (a * a b * b n) { cout a b endl; found true; } } } if (!found) { cout None endl; } return 0; }易错点分析第一枚举范围的边界。外层用a * a n是因为如果a²已经等于n那a必须为0才能满足a²b²n而题目要求正整数所以这种情况可以直接排除。内层用b * b n是因为b²可能恰好等于n此时a为0不过内层b从a1开始所以实际不会出现a0的情况但把条件写成逻辑上没有漏洞也不影响运行。第二去重与排序。内层循环从a1开始天然保证了ab不会出现(3,4)和(4,3)重复输出的问题。又因为外层a从小到大遍历内层b从小到大遍历输出顺序自然满足“按a从小到大”不需要额外排序。第三输出格式。找到一组就输出一行每组输出用空格分隔。最后如果没找到记得输出None大小写要和样例完全一致写成none也会被判错。复杂度和数据范围外层约sqrt(n)次内层约sqrt(n)次整体复杂度约O(n)n最大10000时计算量很小运行飞快。3. 考场实用策略读题、调试与提交的细节3.1 做题顺序和时间分配我见过太多考生在考场上死磕一道题结果后面明明更简单的题没时间写。二级的编程题难度有梯度通常第一道偏基础第二道中等第三道可能带一点弯。我的建议是拿到卷子先花三分钟把所有编程题通读一遍不用细想只判断每道题的难度顺序。然后按“简单→中等→难”的顺序做题先把最有把握的分拿稳再回头琢磨难题。选择题也是一样先跳过卡壳的不要在一道题上耗超过两分钟。编程题的时间分配上我给学生的习惯参考是简单题压到20分钟内中等题25到30分钟难题实在想不出来就写暴力解法拿部分分。还有一个小技巧动笔写代码之前先花两分钟把题目给的样例用笔算一遍。样例不只是给你测试代码用的它还能帮你确认自己对题意的理解是否正确。比如报数出圈那道题你先手动推出输出序列再去写代码写完后一对比就知道思路对不对了。3.2 边界检查清单与样例自测很多考生都有这样的经历样例一遍过提交后零分百思不得其解。问题几乎都出在边界情况没有处理。我建议你把下面这份边界检查清单记下来每道编程题写完都对照着自查一遍输入数据最小时是否正常比如n1、m1、字符串长度为1。输入数据最大时是否越界或超时数组大小是否足够容纳最大范围输入包含边界值时是否正常比如n等于10000、字符串全是空格、数字为负数。输出格式是否严格匹配行尾有没有多余空格输出的是大写还是小写该换行的地方换行了吗循环边界是还是下标是0开始还是1开始自测不能只跑一遍样例就收工。我给自己定的标准是“三个一”复制样例测一次构造最小输入测一次构造最大输入测一次。拿字符串那道题举例样例测完再输入单个字符“A”测最小情况再输入长度200的全字母字符串测最大情况三个都过了才敢提交。3.3 从错误信息到修复编译错误、运行时错误与答案错误考场上一旦提交报错不要慌先看错误类型再定位。编译错误是最常见的也好修。看到expected ‘;’就是漏了分号看到‘x’ was not declared就是变量名拼写不一致或没有定义看到missing terminating character就是字符串引号没闭合。这类错误信息一般会精确到行号逐个修复即可。运行时错误里最典型的是段错误Segmentation fault和浮点异常Floating point exception。段错误基本可以锁定为数组越界比如定义int a[100]却访问a[100]浮点异常多半是除零最常见的就是取模运算中模数为0比如 m 为 0 时执行cnt % m。检查一遍数组大小和取模边界就能解决。答案错误最隐蔽因为程序能跑输出也不为空只是和目标不一致。这时候不要瞎猜在关键变量后面加cout打印中间值比如报数题里打印cnt和idx的变化看它和自己的预期是否吻合。肉眼追踪两遍循环基本能定位到是状态更新顺序出错还是输出格式不对称。4. 备考路线与常见问题实录4.1 三个月从零备考怎么安排如果你是从零开始准备二级三个月是相对稳妥的周期。第一个月集中过语法基础把变量、类型、输入输出、if/else、for/while 彻底弄明白不需要做难题每天写十道语法小题就够了目标是不看参考书能独立写出“判断素数”“求最大公约数”这类小程序。第二个月进入数组、字符串、函数和简单结构体每天一到两道综合题以枚举和模拟为主。这个阶段最重要的是训练“把题意翻译成逻辑”的能力遇到不会的题先自己想十分钟再看答案看完答案必须自己重新写一遍不能直接复制。第三个月开始按考试节奏限时训练每周至少完整做一套模拟题题目做完后要整理错题本。错题本不用抄题只需要记录三句话这题考什么知识点我卡在哪一步下次怎么避免考前一周不要再做新题只看错题本和自己的易错清单。这个节奏看起来简单但能坚持下来的人并不多。我带过的考生里按这个节奏走的绝大多数都能在编程题上拿到满意的分数反而是临时突击刷题的人往往在考场上碰到变式就懵。4.2 二级最容易踩的五个坑第一输入包含空格却用cin string。这是字符串处理题最大的坑。cin按空白字符分隔输入只要遇到空格或换行就停止根本读不到完整的一行。读整行字符串必须用getline。第二数组开得不够大。题目说n最大100有人就开int a[100]但C数组下标从0开始最大有效下标是99如果访问a[100]就越界了。我的习惯是比最大值多开10到20个空间比如n≤100就开105这样既能容纳数据也给边界操作留出余量。第三输出格式和样例不一致。样例输出的数字之间用一个空格分隔末尾没有多余空格有人写循环的时候在每轮输出后都打印空格最后一行就多出一个空心字符。机评系统对这种格式错误是零容忍的。第四循环边界条件写错。用还是i0还是i1多一个少一个都会导致答案偏差。每次写完循环先确认循环的次数是否和自己的预期一致。第五变量复用混乱。同一个变量既用来计数又用来存储结果比如一边用cnt统计出圈人数一边又用它做中间计算一个改动牵动全局排查起来非常痛苦。变量一多就换个名字别懒。4.3 冲分建议与实际体会二级考试有一个特点特别值得利用编程题是按测试点给分的不是“全对或全错”。也就是说即使你的算法只能处理部分数据把这些数据对应的测试点全部通过也能拿到不少分数。所以遇到一时想不出最优解的题先写一个暴力解法提交拿稳部分分再去考虑优化。我在帮考生复盘202603批次的时候发现真正拉开差距的不是谁做出了最难的题而是谁在简单题上没失分。选择题的前半部分都是基础语法这些分丢了非常可惜编程题第一道也大多是字符串或简单模拟难度不高细心就能过。把基础分全拿到二级已经稳了。最后说说个人感受。我带过不少二级考生有的小朋友聪明但粗心考试常常差五分也有的基础一般但每次写完代码都认真跑边界测试反而一次通过。二级真的不需要天赋它考的是习惯读题仔细一点边界多想一步输出格式多看一眼这些习惯养成了三级四级都会受益。如果你能把上面三道题自己重新写一遍再找一套模拟卷限时完成过二级基本没什么悬念。