
又到了一年一度的校招季后台不少学弟学妹在问“技术类在线笔试到底考什么”“怎么准备才能不挂”。这让我想起自己当年参加小红书2019年校园招聘技术类在线笔试第一批的经历那次笔试给我留下的印象挺深——题目难度分布合理、节奏紧凑、平台操作也比较规范而且有几道题非常典型值得拿出来复盘。我尽量把当时现场的情况还原出来包含完整的题目拆解、思路推导、代码实现和踩坑记录。如果你今年也要投技术岗校招这份内容应该能帮你少走不少弯路。1. 笔试基本信息与题型分布1.1 笔试平台、时间与整体安排在线笔试用的是牛客网的平台这个在当年已经算是校招标配了。一共4道编程题限时120分钟全程监控开启需要共享屏幕。我当时用的是C也有人用Java、Python平台都支持。说一下我当时对这场笔试的整体感受题目不算特别偏但有一个很明显的特征——前三题是“保底题”最后一题是“区分题”。前三题只要能扎实刷过《剑指Offer》或者LeetCode热门题基本都能AC最后一题如果不提前接触过类似模型很容易卡在超时上。时间分配上我个人建议是这样题型建议用时目标第1题简单题15-20分钟必须AC第2题中等题20-30分钟尽量AC第3题中等偏难题25-35分钟尽量AC第4题压轴题30-40分钟尽力拿部分分1.2 题目难度梯度分析4道题难度是明显递进的。第1题是典型的字符串模拟题考察基本编码能力第2题是数组处理加双指针思维第3题是动态规划模型不复杂但边界条件多第4题是数据结构优化题暴力解法好想但过不了全部数据。这里我要多说一句很多人看见“难度递进”就理解为“从第1题做到第4题”这是最大的误区。在线笔试的判分规则通常是按通过用例数给分而不是按题号加分。所以一个很现实的策略是先把4道题都看一遍每道题先用暴力解法拿部分分最后再回来优化。这样哪怕最后一题没AC至少能拿到60%-70%的用例分。后面我会结合具体题目详细讲解这个策略怎么落地。2. 四道真题完整复盘与解题思路这一部分我尽量把题目、思路、代码、踩坑点全部展开不省略关键推导过程。题目是我凭记忆还原的核心数据范围和考察点是可以确定的。2.1 第1题字符串连续字符压缩题目描述给定一个字符串将其中连续重复的字符压缩为“字符出现次数”的形式。例如aaabbbcddd压缩后为a3b3c1d3。要求不改变字符原有顺序且压缩后的字符串长度必须小于原串长度否则返回原串。字符串长度不超过1000。这道题属于“签到题”的级别。我当时是先写一个循环遍历记录当前字符cur和计数cnt遇到不同字符时把cur和cnt拼到结果里最后再做一次长度比较。def compress(s: str) - str: if not s: return s res [] cur s[0] cnt 1 for i in range(1, len(s)): if s[i] cur: cnt 1 else: res.append(cur str(cnt)) cur s[i] cnt 1 res.append(cur str(cnt)) compressed .join(res) return compressed if len(compressed) len(s) else s这里有一个很容易忽略的点题目要求“压缩后长度必须小于原串长度否则返回原串”。如果原串是abc压缩后是a1b1c1长度反而更长必须返回原串。我见过有人栽在这个“小于”还是“不大于”的判断上如果是等于的情况返回原串照样能过但其实题目明确写的是“小于”。复杂度是 O(n)没啥悬念。这道题就是考察你能不能把逻辑写干净不要出边界 bug。2.2 第2题数组按频率从高到低排序题目描述给定一个整数数组请按照元素出现频率从高到低排序。如果两个元素的频率相同则按元素值从大到小排列。数组长度不超过10万元素值范围在 -10^9 到 10^9 之间。这道题的关键在于排序规则的组合。先统计频率然后对元素去重后排序排序的比较函数需要同时考虑两个维度频率降序 元素值降序。我当时的做法是先用哈希表统计频率然后把键值对转成列表用自定义比较函数排序最后按顺序输出。from collections import Counter def freq_sort(arr): cnt Counter(arr) # 按频率降序频率相同按元素值降序 items sorted(cnt.items(), keylambda x: (-x[1], -x[0])) res [] for val, freq in items: res.extend([val] * freq) return res这个做法本身没什么问题时间复杂度是 O(n log n)注意看数据范围长度10万O(n log n) 是稳的。但有几个细节值得提第一比较函数怎么写。Python 里用key参数时可以取负号来模拟降序因为元组是字典序比较。(-x[1], -x[0])表示先按频率降序再按值降序。C 的话用sort传入cmp函数注意cmp要写成严格弱序。第二元素值范围很大有负数所以你不能用“数组下标计数”的方式必须用哈希表。这道题其实想考察的是你会不会用哈希表做统计以及你会不会写自定义排序规则。属于面试笔试中的“万金油”题。2.3 第3题最小编辑距离变种题目描述给你两个字符串 A 和 B你可以对 A 进行三种操作插入一个字符、删除一个字符、替换一个字符。求将 A 变成 B 所需的最少操作次数。两个字符串长度均不超过1000。看到“最小编辑距离”不用犹豫直接往动态规划上想。这是 LeetCode 72 题的原题模型。定义dp[i][j]表示将 A 的前 i 个字符变成 B 的前 j 个字符所需的最小操作次数。初始化时dp[i][0] i删除 i 次dp[0][j] j插入 j 次。状态转移分两种情况如果A[i-1] B[j-1]说明当前字符不用额外操作dp[i][j] dp[i-1][j-1]如果不相等考虑三种操作的最小值删除A[i-1]dp[i-1][j] 1插入一个字符匹配B[j-1]dp[i][j-1] 1替换A[i-1]为B[j-1]dp[i-1][j-1] 1def min_distance(a: str, b: str) - int: n, m len(a), len(b) dp [[0] * (m 1) for _ in range(n 1)] for i in range(n 1): dp[i][0] i for j in range(m 1): dp[0][j] j for i in range(1, n 1): for j in range(1, m 1): if a[i - 1] b[j - 1]: dp[i][j] dp[i - 1][j - 1] else: dp[i][j] min( dp[i - 1][j] 1, # 删除 dp[i][j - 1] 1, # 插入 dp[i - 1][j - 1] 1 # 替换 ) return dp[n][m]这道题最大的坑不在算法本身而在字符串长度的边界。1000 的长度二维数组是 1001×1001大约 100 万个元素内存完全没问题。但如果你写dp [[0] * (m 1)] * (n 1)那就是把同一行引用了 n1 次修改任意一行都会影响其他行结果全错。这种 Python 初始化写法在笔试里是高频翻车点建议用列表推导式。另外在这个基础上有些笔试题会加一个“只允许插入和删除不允许替换”的限制那状态转移就只剩两个分支。我当时写题的时候先看了题目问的是“三种操作”还是“两种操作”确认完再动笔这能避免返工。2.4 第4题最大子数组和模 m 问题题目描述给定一个长度为 n 的数组n 不超过 10^5和一个正整数 m求数组中某个连续子数组的和模 m 的最大值。这道题是经典问题的一个变体最大子数组和的进阶版。普通的最大子数组和可以用 Kadane 算法 O(n) 解决但加上“模 m”之后贪心失效了因为局部最优不能推导出全局最优。我先说暴力思路枚举所有子数组计算区间和再取模时间复杂度 O(n^2)n 是 10^5 的话完全跑不动。笔试时如果只能写暴力大概只能过 20%-30% 的用例。正解需要数学推导。先算前缀和pre[i] (a[0] a[1] ... a[i-1]) % m那么区间[l, r]的和模 m 等于(pre[r] - pre[l]) % m。要最大化这个值可以分两种情况讨论如果pre[r] pre[l]那么(pre[r] - pre[l]) % m pre[r] - pre[l]想让值最大就是让pre[l]尽量小如果pre[r] pre[l]那么(pre[r] - pre[l]) % m pre[r] - pre[l] m想让值最大就是让pre[l]尽量大遍历前缀和数组时我们需要在已经遍历过的前缀和中找到“比当前值大的最小值”和“全局最小值”。这里就需要有序容器。C 可以用set或mapPython 可以用bisect来维护有序列表。from bisect import bisect_right, insort def max_subarray_mod(arr, m): pre 0 ans 0 # sorted_list 维护已出现的前缀和 sorted_list [] insort(sorted_list, 0) # 前缀和初始为0 for x in arr: pre (pre x) % m # 找到第一个 pre 的元素 idx bisect_right(sorted_list, pre) if idx len(sorted_list): ans max(ans, (pre - sorted_list[idx] m) % m) else: # 没有比 pre 大的那就用全局最小 ans max(ans, pre) insort(sorted_list, pre) return ans有人可能不理解pre的更新逻辑pre其实是“当前前缀和模 m”每一次更新完我们就看一下“跟哪个历史前缀和相减能让结果最大”。上面代码里(pre - sorted_list[idx] m) % m处理的是“历史前缀和比当前大”的情况因为模运算可能是负数所以要加 m 再取模。这道题是典型的数据结构优化题考察点包括前缀和、模运算、以及有序容器的高效维护。笔试时如果只写暴力能拿部分分写出有序集合优化版本基本就是头部选手了。3. 时间分配策略与做题顺序3.1 遇到难题先跳先拿基础分我在上面已经提到在线笔试的判分跟“一锤定音”的考试不一样它按通过用例数算分。这意味着你花 40 分钟死磕最后一题不如用 10 分钟写一个暴力版本拿 30% 的用例分然后把时间拿回去确保前两题 100% AC。按照当时的题目难度分布我的实际时间安排是先花 5 分钟快速浏览全部 4 道题确认难度梯度和自己熟悉的题型第 1 题直接 AC用时约 12 分钟第 2 题看完就知道是哈希表 排序约 20 分钟 AC第 3 题是最小编辑距离想到了 DP 但仔细想边界条件约 30 分钟提交第 4 题先写了暴力版过了一部分用例再优化成有序集合版最后大概剩 10 分钟这里有个细节值得分享写第 3 题 DP 的时候我没有一上来就写完整代码而是先在草稿纸上把 dp 数组的维度、初始化的含义、状态转移的三种情况列清楚再动键盘。这样看起来浪费了 3-5 分钟实际上避免了写出“看起来对但边界全错”的代码。3.2 笔试代码的调试技巧在线笔试的调试环境和本地 IDE 还是有区别的。牛客网的编辑器没有智能提示也不能打断点所以你需要在写代码时格外注意变量名和缩进。我当时发现自己第 3 题有个边界 bug当字符串为空时dp[0][j]和dp[i][0]的初始化会出问题。这个是在本地跑测试用例时发现的我加了一个if not a or not b的提前处理才稳下来。建议你们在提交前至少测试这几类边界输入为空的情况数组/字符串长度为 1 的情况全部元素相同的情况所有元素都不同的情况数据量最大的极端情况看一眼代码是否超时3.3 关于“部分分”的执念很多人有个心理误区觉得写暴力解法“丢人”或者觉得“要么 AC要么不做”。这是校招笔试里最亏的心态。在线笔试是机器评分暴力解法只要用例跑得过就有分。哪怕只有 20% 的用例通过在面试官筛选简历时这个分数可能比“0 分”强十倍。我当年参加过不少笔试见过太多人卡在最后一题“想不出最优解就不提交”结果是那道题 0 分。反过来有些人第一眼就知道没有最优解思路先写个暴力拿到部分分再慢慢优化。后者的通过率明显高很多。4. 笔试题背后的考点与能力模型4.1 从题目反推面试官想考察什么校招在线笔试不像竞赛题那样追求“偏、怪、难”它有很明显的筛选逻辑。从这 4 道题来看面试官其实在考察几个基础能力第一题考“编码基本功”。字符串压缩是一个纯模拟题没有任何算法含量就是看你能不能写出清晰、无 bug 的代码。这种题挂人的原因几乎都是边界没处理好、循环索引写错、忘记看返回条件。第二题考“对常见数据结构特性的掌握”。哈希表统计频率谁都会但能不能把“多关键字排序”写对体现了你对比较函数和排序稳定性的理解。第三题考“动态规划的基础建模能力”。编辑距离是 DP 入门必学题但很多人只看过题解没有自己完整推导过状态转移。笔试一紧张就容易漏掉“替换”操作或者初始化写错。第四题考“对经典问题的变形理解”。如果只是背过最大子数组和的 Kadane 算法遇到模 m 版本会直接懵。这题要的不是“背题”而是数学推导能力——知道前缀和知道取模的性质才知道如何用有序集合去优化搜索。4.2 笔试与面试之间的衔接关系小红的面试流程对笔试成绩还挺看重的笔试分数高面试时基础知识被问的概率会相对小一些更多会问项目经历和系统设计笔试分数低面试官可能会多问算法题用来现场验证你的真实水平。所以我会建议求职者把在线笔试当成“一次免费的全真模拟面试”不是为了分数而写题而是为了检验自己到底哪块知识体系还不牢固。笔试暴露出的盲区如果能在面试前补上反而比“蒙对一道题”更有价值。4.3 如何有针对性地准备这类笔试如果你是今年要参加校招的同学我建议不要盲目刷题而是按模块来准备。从这 4 道题可以看出字符串处理、哈希表与排序、动态规划、前缀和与数据结构是出现频率极高的大类。你可以建立一个自查表知识点模块常见题型代表例题字符串数学模拟压缩、翻转、循环移位、括号匹配LeetCode 443 / 151哈希表 自定义排序频率排序、Top K、同字母异分词LeetCode 347 / 49动态规划编辑距离、背包问题、最长公共子序列LeetCode 72 / 1143前缀和 有序容器子数组和类问题、区间查询LeetCode 560 / 974把这些基础模型吃透再刷 10-20 道同类变种题笔试的“保底分”基本就能拿到。剩下那些压轴题更多考察临场数学推导能力遇到没见过的新题也不要慌先暴力再优化拿到部分分就是胜利。4.4 平台操作规范与远程笔试注意事项这里单独提一下在线笔试本身的“非算法风险”。我记得当年这场笔试要求提前 15 分钟进入系统做设备检测包括摄像头、麦克风、屏幕共享权限。有时候插件装不上或者浏览器版本不兼容会直接影响考试这种事故完全不值得。以下几点是我自己多次笔试总结出来的强烈建议收藏提前一天测试摄像头和屏幕共享不要等开考前 5 分钟才慌浏览器建议用 Chrome 或官方指定的版本关掉所有无关插件在线笔试期间不能切换出浏览器否则容易被判作弊这个务必注意准备一张草稿纸和笔不是所有题都适合直接在编辑器里想如果家里网络不稳定能去学校机房或网吧就尽量去掉线比不会做更冤5. 常见错误与高频翻车点汇总5.1 笔试现场最容易犯的 5 类错误我前后参加了大概 10 场校招在线笔试见过也踩过不少坑这里总结一个“高频翻车清单”错误类型典型案例解决思路读题不清忽略了“压缩后长度必须小于原串”的返回条件动笔前先把题目翻译成自己的话确认输出要求边界遗漏字符串为空、数组长度为 1 时直接越界写完代码先跑边界用例再提交时间分配失衡最后一题死磕 40 分钟前两题草草提交先快速浏览 4 题按性价比分配时间环境不熟悉编辑器没有自动补全语法报错定位慢提前熟悉牛客网等平台的在线编辑器过度自信代码一跑样例通过就直接提交没有测试边界本地或脑子里跑 3-4 组用例再交5.2 正确率 vs 速度的取舍在线笔试有两种典型选手一种是“求稳型”每道题都要跑测试用例、反复检查最后可能只做完 2 道另一种是“求快型”写完就交速度很快但错误率高AC 率上不去。说实话这两种都容易挂。校招笔试的判分不是“做对两道就给两道满分”而是“每道题按通过用例比例给分”。所以最优策略是先保证简单题 100% 正确中等题争取 100%压轴题拿到部分分。为了达到这个目标你其实不需要特别快的速度而是需要稳定的准确率和良好的时间分配能力。5.3 复盘方法笔试结束后怎么最大化利用这次经历笔试结束后别光看分数。分数只是结果更重要的复盘是哪些题是因为知识盲区没做出来哪些题是因为马虎丢分哪些题是时间不够。我自己每次笔试后会做一个简单表格记录每道题的掌握情况、失误原因、改进方向然后对照刷题列表补漏。这种复盘方法看起来麻烦实际坚持下来收益很大。笔试的意义不只是“拿 offer”它也是对你当前算法水平的一次客观体检。校招周期长前期笔试暴露的问题完全有时间在面试前补齐关键是你愿不愿意花这个功夫。6. 经验小结什么是真正有效的刷题方式最后分享一点我个人对“刷题”这件事的体会。有人喜欢一天刷 10 道题追求数量上的快感但这样往往只是“见题型背套路”题目稍微变形就卡住。我更推荐“每题吃透”的方式拿到一道题先自己想思路想不出再参考答案理解后再关掉答案自己写一遍最后把这道题的考点整理到自己的笔记里。这个流程走完才算真正掌握了一道题。这次的 4 道题里第 4 题给我的启发最大。它是那种“暴力解法谁都能想到但最优解需要数学推导和数据结构支撑”的题目。后来我在准备面试时专门把所有“前缀和取模”相关的题目都刷了一遍比如连续子数组和、可被 K 整除的子数组等。刷题要刷同类变种不要只刷孤立的题号。只有把一道题背后的数学模型吃透遇到新题时才能举一反三。希望这篇复盘对正在准备校招的你有所帮助。如果你们在笔试中碰到其他有意思的题目欢迎回来一起讨论。