
2018年春天牛客网推出校招笔试一模我第一时间报了名。当时身边很多人只是把它当成一次普通的在线编程题练习考完看一眼分数就翻篇了。我后来发现这份B卷的价值远不止分数——它几乎把校招笔试里最常出现的题型、最容易踩的坑全浓缩在了两小时里。这篇文章不是真题搬运而是基于B卷常见题型方向做的一次完整复盘哪几类题必须拿满分哪些题是用来筛选思维能力的现场提交时最容易在哪些细节上翻车。如果你正在准备春招、暑期实习或者刷了不少题但心里没底这篇复盘应该比单纯再刷十道题更有用。1. 先看全局2018年一模B卷到底在考什么1.1 从A卷到B卷的定位差异一模通常分A卷和B卷A卷偏热身B卷的难度会明显高一截。参加过的人应该都有感受A卷前两三道基本是送分题考察的是你“会不会写基础语法”到了B卷送分题还在但后面会混入更多需要动脑子的题目尤其是字符串边界处理、贪心策略和入门动态规划。我记得B卷整体题量大概在10到15道之间考试时长两小时左右。题量看着不多但如果每道题都磨蹭时间根本不够用。当时我身边不少同学A卷能提前半小时交卷到了B卷却卡在第二三题就慌了。原因也很简单B卷更强调“代码实现能力边界处理意识”你不仅得想出解法还得在限时内写出能扛住OJ各种边界用例的代码。1.2 高频考点大致分布虽然不能说每套一模B卷题目完全一样但根据我和几个一起刷题的同学复盘考点分布是有明显规律的。下面这张表是我根据常见题目方向整理出来的不是真题原文但对备考很有参考价值考点类型大致题量考察重点难度定位字符串处理3道左右反转、子串、字符统计、单词切分送分到中等数组与模拟3道左右排序、指针移动、矩阵遍历送分到中等贪心 / 排序1-2道区间、调度、局部最优推导中等偏上动态规划1-2道最大子数组、最长上升子序列等基础模型中等偏上数学 / 逻辑1道左右进制转换、素数判定、规律找式送分到中等这个分布和春招笔试的主流风格是吻合的。如果你发现自己某个模块特别薄弱那么一模就是最好的“探照灯”能把你平时刷题没注意到的盲区照出来。1.3 难度梯队与时间分配建议从实战角度我把B卷的题目分成三个梯队第一梯队是签到题大概占2到3道主要考察输入输出、循环、数组基本操作。这类题的目标是“又快又对”最好在10分钟内搞定。第二梯队是字符串和模拟题大概3到4道需要细心处理边界条件。这里容易出现“思路秒杀、代码翻车”的情况建议每道题控制在20分钟左右留出时间反复检查。第三梯队是贪心和动态规划通常1到2道难度取决于你是否见过类似的模型。这类题如果5分钟内没有清晰思路先跳过最后再回来啃不要死磕。我当时定的节奏是前20分钟做掉签到题和前两道模拟题中间50分钟处理剩余模拟题和字符串题最后50分钟集中攻克DP、贪心和那道综合题。这个节奏不一定适合所有人但“先保底、再突破”的思路是通用的。2. 字符串与模拟题送分题里的失分重灾区2.1 典型题目方向与读题陷阱字符串处理在B卷里出现频率极高。常见的出题方向有反转字符串中的单词顺序、统计出现次数最多的字符、判断是否存在回文子串、去除重复字符后按字典序排序。这些题单独拿出来都不难但在线上OJ环境里失分率反而特别高原因就一个字边界。我印象很深的一道题型是“反转字符串中的单词顺序”。题目会给一句英文要求把单词顺序倒过来比如I am a student要变成student a am I。很多人的第一反应是先把整个字符串反转再逐个单词反转回来这个思路是对的但现场写起来很容易忽略两个细节一是字符串前后可能有多余空格二是单词之间可能存在多个连续空格。2.2 最容易被卡住的三个边界第一个边界是空串。有些题目不会明确说输入一定非空如果代码里直接访问下标空串就会导致越界。第二个边界是连续空格如果不做处理切出来的“单词”会混入空字符串导致结果错误。第三个边界是输出格式比如最后不能有多余空格很多OJ对末尾空格是判错的。处理空格问题我推荐一个省心的技巧用C的istringstream它天然会忽略连续空格按空白字符切分单词。Java里可以用split(\\s)Python里更简单直接用split()。但要注意C的istringstream在遇到中文或非ASCII字符时行为可能不符合预期所以做题前先确认题目输入范围通常校招笔试都是纯英文字母加数字。2.3 一个标准示例反转字符串中的单词以“反转单词顺序”这道经典题为例我最常用的写法是这样#include bits/stdc.h using namespace std; string reverseWords(string s) { string res ; istringstream iss(s); string word; vectorstring words; while (iss word) { if (!word.empty()) { words.push_back(word); } } for (int i (int)words.size() - 1; i 0; --i) { res words[i]; if (i 0) res ; } return res; }这里的核心逻辑是先用istringstream按空白切好单词存入words再从后往前拼接。每次拼接时判断i 0才加空格避免末尾多一个空格。word.empty()的保护虽然在这种场景下几乎不会触发但写上更稳妥。如果同样的题用Python写代码更短def reverseWords(s: str) - str: words s.split() return .join(words[::-1])Python的split()默认就是按任意空白字符切分并且自动忽略空串输出时再用空格join回去整道题两行解决。这也是为什么我后来练手经常用Python笔试时如果允许选择语言这类字符串题用Python确实省心。2.4 现场做题的复盘教训一模那场我在这类题上丢过分复盘时发现了三个问题。第一个是读题太快没注意到题目要求“输出单词间保留单个空格”我以为原样输出就行。第二个是测试用例造得太少只测了常规句子没测前后空格和全空串。第三个是心态问题写完后没有回头读一遍代码结果把words[i]写成了words[i-1]这种低级错误在OJ上就是整题零分。从那以后我给自己定了个规矩每道字符串题至少造三组特殊用例——空串、全空格、极限长度串。这个习惯一直保留到现在确实帮我少丢了很多冤枉分。3. 贪心和动态规划B卷拉开差距的两座山3.1 贪心题的识别标志一模B卷里贪心题不像字符串题那么直白它通常会包装成一个生活化的场景比如“安排最多的活动”“用最少的硬币凑出某个金额”“切分木板的最小代价”。这类题有一个共同特点看起来需要尝试很多种组合但如果你能找到一个局部最优策略并且这个策略能一步步推出全局最优那么它就是贪心题。识别方法其实很直接如果题目问的是“最多”“最少”“最短”这种极值问题而且候选决策之间没有复杂的相互依赖关系可以先往贪心上想。考场上的建议是先举几个小例子验证策略比如“每次选结束时间最早的活动”是否真的能安排最多活动如果小例子都能通过就大胆写不要过度纠结证明。我见过不少人在考场上明明想到了贪心策略却因为怕有反例而不敢写最后浪费大量时间。其实在笔试时限下正确的策略是用小样例验证后快速实现而不是在草稿纸上做完整数学证明。当然平时训练时还是要补证明能力但那是另外一回事。3.2 DP题的读题信号动态规划在B卷里出现的概率很高但难度不会一下拉到太难的程度。最常见的两个基础模型是最大子数组和与最长上升子序列。这两类题目有一个非常明显的信号当前状态可以由之前的状态直接推导而且题目要求的最优解具有“重叠子问题”的结构。以最大子数组和为例题意通常是一组数中找出连续的一段使它们的和最大。最直观的做法是双层循环枚举所有连续区间时间复杂度O(n²)但B卷往往会把数据范围调到n10的5次方甚至更大O(n²)必死。这时候就需要用DP。3.3 状态定义与转移手把手最大子数组和的状态定义非常经典设dp[i]表示以第i个元素结尾的最大子数组和。转移方程就一行dp[i] max(nums[i], dp[i-1] nums[i])这个方程的意思是以当前元素结尾的最大子数组要么只包含当前元素自己要么接在前一个元素结尾的最大子数组后面。最后答案就是所有dp[i]里的最大值。如果要讲得更直观你可以把它想象成在连续爬坡如果前面的累积收益是负的那不如从当前重新起步。代码实现可以进一步优化因为dp[i]只依赖dp[i-1]完全不需要开数组class Solution { public: int maxSubArray(vectorint nums) { int cur 0; int ans INT_MIN; for (int x : nums) { cur max(x, cur x); ans max(ans, cur); } return ans; } };这个模式我推荐所有准备笔试的同学背下来因为它不仅是最小代码量的写法还能避免数组越界问题。我第一次在牛客OJ上提交时用的是开dp数组的写法后来发现用滚动变量不仅更省空间逻辑也更不容易出错。3.4 先写暴力再写优化考场上的救命原则无论DP还是贪心我在B卷现场体会最深的一条原则是如果你不能立刻想到最优解先写一个暴力解法保底。理由很简单——暴力解法虽然可能超时但至少逻辑正确能拿到一部分测试点的分而空着不写,一分都没有。暴力写完后再尝试用滚动数组、二分、排序等手段优化。比如最长上升子序列暴力DP是O(n²)优化版用贪心二分可以做到O(n log n)。但在考场上如果不确定优化版能否写对我宁可先交O(n²)版本等做完全部题目再回头看这道题是否能优化。这种“先保底、再升级”的策略能最大化整张卷子的得分。4. 提交运行时那些坑超时、溢出与静默错误4.1 时间复杂度自测B卷的题面一般会给出数据范围这是判断时间复杂度的关键线索。我当时的经验是1秒的时间限制内大约能运行1e7到1e8次基本操作。也就是说如果n是1万O(n²)勉强能过如果n是10万甚至100万O(n²)基本必超时。数据范围nO(n)O(n log n)O(n²)1万安全安全勉强10万安全安全危险100万安全可以必挂做题前先花10秒估算一下复杂度能避免很多冤枉的超时。比如一个双层循环遍历数组n是10万那就是1e10次操作任何OJ都扛不住。4.2 整数溢出的坑B卷里经常出现累加、乘法运算C选手尤其要小心int溢出。很多人在本地测试时一切正常一提交就是WA原因就是数据范围一大int直接爆掉了。最典型的场景是求数组和、求阶乘、求组合数。我的习惯是只要题面数据的绝对值可能超过10的4次方或者涉及累加求和统一用long long。不要觉得写long long显得不专业在OJ上这叫稳妥。还有一类坑是负数情况下的取模运算C里负数取模的结果可能是负的如果题目要求输出非负就得先加模数再取模。4.3 输入输出格式细节输入输出是B卷另一个失分点。常见的问题包括多组测试数据要用while (cin n)循环读入不能只读一次输出要求每个结果占一行不能用空格分隔要求精确到小数点后几位时用printf或iomanip设置格式。我记得一道模拟题题目要求输出两个数字之间逗号加空格分隔我只写了逗号没加空格结果WA。这种错误在本地根本看不出来因为人眼会自动忽略格式差异但OJ不行。所以提交前一定要逐字核对题目输出格式尤其是在“注意”“提示”这些地方。4.4 现场调试三板斧第一板斧是造边界用例空串、零、最大值、最小值、极端输入每道题提交前都过一遍。第二板斧是打印中间状态在代码里临时加cout或printf输出关键变量的值确认逻辑走到哪一步出了问题定位后立刻删掉。第三板斧是用assert检查不可能出现的情况比如数组下标应该在[0, n)之间如果断言失败说明前面的逻辑有bug。这三板斧看着简单但真到考场上能救命。我B卷最惨的一次是字符串题多了一个换行符肉眼完全看不出来最后用printf([%s], res.c_str())一打印才发现末尾多了个不可见字符。5. 模考成绩出来后怎么用从分数段到刷题路线5.1 分数段对照表模考成绩出来后最忌讳的事情是看一眼总分就关掉页面。正确的做法是先按题型统计得分率再对照自己的目标岗位判断短板。我整理了一个粗略的分数段参考得分率状态评估建议行动30%以下基础和心态都还需要练回到入门题先把输入输出、数组、字符串基本功打牢30%-50%能写基础题但模型题薄弱重点补排序、二分、贪心和简单DP50%-70%常见题型基本掌握但容易被边界和复杂场景卡住系统做题每道题都要求自己处理边界情况70%以上笔试能力较强可以冲击难题刷真题和综合题训练解题速度对照完分数段再去看具体错了哪些题是因为题意理解错还是解法复杂度不够还是代码写错但思路正确这三种错误的复习方式完全不同。我当时的复盘方式是建一个Excel表列了题号、错误类型、错误原因、正确思路、可复用的技巧五列每套卷子都填。虽然有点麻烦但一个月下来自己常犯的错误一目了然。5.2 一个可执行的四周补强计划如果你一模B卷做完后觉得自己还没到理想水平这里给你一个我实际操作过、也推荐给学弟学妹的四周计划。第一周专攻字符串和模拟每天刷5道左右字符串题重点练split、substring、反转、双指针这些操作第二周专项练排序、二分和双指针重点理解有序数组和指针移动的套路第三周开始接触贪心和DP最大子数组、最长上升子序列、零钱兑换这几个模型必须滚瓜烂熟第四周做牛客真题模考每周至少3套每套都严格按照笔试时间和心态要求自己。这个计划的核心思路是用一周时间集中突破一个题型而不是每天泛泛地刷10道不同题。题型集中训练的好处是能快速总结规律形成条件反射考试时看到题目就能立刻锁定考点。5.3 这套题对面试和后续学习的长尾价值很多人觉得模考卷做完了就算完成任务其实不是。B卷里的字符串题、DP题、贪心题在后续面试手撕代码环节出现概率极高。尤其是“反转字符串中的单词”“最大子数组和”这类的题我在面试里遇到过不止一次。模考时走过的坑面试时就能少踩一个。而且这套题型训练对后续各类编程等级考试也有帮助。我今天还看到有人在问python一级编程题的事其实不管是牛客笔试还是等级考试基础的数据处理、字符串切分、循环和条件判断都是同一套底层能力。不要因为年份变了、平台变了就觉得要重新学一遍基本功到位了任何考法都能快速适应。最后分享一个我从一模之后一直保留的习惯每套题考完必须留10分钟做复盘笔记。不是把代码贴一遍就完事而是记录“我为什么卡住”“下次怎么做能更快”。这个动作在当下看着不起眼但坚持几个月后你会发现同类错误越来越少解题速度也会有明显提升。就像一模B卷那道让我丢分的字符串题它教给我的不是某一个函数怎么用而是“读题后再动手、提交前必查边界”这十二个字。这套卷子真正的价值也正是从这些教训里长出来的。