news 2026/9/9 6:25:37

牛客模考2019一模编程题解析:字符串、数组与DP笔试技巧

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
牛客模考2019一模编程题解析:字符串、数组与DP笔试技巧

提起牛客模考,很多人第一反应是“刷题刷到吐”。但2019年这套一模编程题集合,我到现在还会翻出来重刷。原因是它不考偏题怪题,考的净是字符串处理、数组模拟、基础动态规划这些笔试基本功。题目看着都不难,但真正上了笔试环境,能一次通过的人其实不多,因为坑都藏在细节里:边界条件、输入输出格式、空值处理、数据范围,任何一个没想清楚就白费半小时。

这套编程题集合特别适合两类人:一类是准备校招笔试、想在牛客上练手找感觉的同学;另一类是刚学完编程基础、想检验自己能不能独立写完整程序的新手。我的建议是,不要只收藏不练习,直接照着ACM模式去写,用标准输入输出跑一遍,再对照解析看自己漏在哪。本文会把整套题的考点分布、典型题目的完整解题思路和代码、笔试现场的排查技巧全部拆开讲清楚,内容偏实操,你可以直接拿去用。

1. 整体考点拆解与备考定位

1.1 2019一模编程题的题型分布与用意

这套题一共八道左右,按难度看是阶梯式上升。前两三道属于“送分题”,考字符串反转、数字统计、字符计数,主要目的是让你进入状态;中间三四道开始上数组操作和模拟,比如数组去重、约瑟夫环、查找只出现一次的数字;最后两道才开始有动态规划的影子,例如爬楼梯、最长连续上升子序列这类经典入门DP。

为什么这么安排?牛客模考模拟的是真实校招笔试环境,真实笔试第一题如果太难,很多人直接心态崩溃,整场考试就废了。所以前面放简单题,既是让你热身,也是考察你在压力下能否快速写出干净代码。而后面的DP题,考察的不是你背了多少模板,而是你能不能把问题抽象成状态转移。这种能力不是临时刷几十道题能补上的,得靠平时积累。

我当时做完这套题最大的感受是:它跟LeetCode那种纯算法题不一样,它更贴近国内笔试的“ACM风格”。题目描述里会带输入输出格式、数据范围,你必须自己处理输入解析,自己处理多组测试数据。很多在IDE里写惯了函数的人,第一次接触这种模式会很不适应,觉得“我逻辑明明对,为什么判题不过”。原因往往就是输入输出没处理好。

1.2 为什么说这类题目最考验真实编程功底

我在各个技术社区见过不少刷题量很大的人,LeetCode能稳定做中等题,但一上牛客笔试就翻车。原因很简单:牛客的编程题是黑盒判题,它不会告诉你哪组数据错了,只给你一个“通过率0%”。这种情况下,你能不能靠自己的经验定位到问题,本身就是编程能力的一部分。

这套一模题正好把这种“笔试真实感”拉满了。你写的程序不仅要算得对,还要抗得住边界输入。例如字符串的连续空格、空字符串、超大整数、数组长度只有1的情况。每年都有人在这些地方翻车,不是不会做,而是没养成“先想边界,再写代码”的习惯。

另外,这套题还有一个隐性的考察点:代码风格。判题系统虽然不看你代码好不好看,但笔试结束后,如果你进入面试环节,面试官可能会翻看你当时的代码。变量名乱写、逻辑堆成一坨、没有注释,即便你AC了,印象分也会打折扣。所以我在下面的解析里,会尽量把代码写得规范一些,方便你也养成好习惯。

1.3 备考资料与刷题顺序建议

如果你现在手头没有完整题单,我的建议是先把经典基础题刷透,再上这套模考。最近几年Python等级考试一级里大量出现的题型——比如字符串反转、数字统计、循环模拟——跟这套一模题的重合度非常高。如果你正在准备Python考级,拿这套题练手也完全合适,因为两者考的底层能力是一样的:读懂需求、拆解问题、把逻辑翻译成代码。

刷题顺序我推荐这样安排:先独立AC一遍,卡住超过20分钟就看提示,但看完提示必须自己重写一遍,不能直接抄代码。AC之后,再看有没有更优解,比如这题用暴力法能过,但数据范围变大之后会不会超时?把每一道题都做“透”,比盲目刷三套题管用得多。这套一模题总量不大,正好适合精刷。

2. 字符串与模拟类题目的高分写法

2.1 题目一:反转字符串中的单词顺序

这道题是整套题里的典型送分题,但也是翻车重灾区。题目描述大概是:给出一句话,单词之间用空格分隔,要求输出单词顺序反转后的结果。注意,不是把每个单词的字母反转,而是把单词的顺序反转,比如输入“I am a programmer”,输出“programmer a am I”。

很多人看到这题第一反应是:用split按空格切分,然后翻转列表,再join。逻辑确实对,但如果你直接写s.split(" "),就会在“连续多个空格”这个测试用例上栽跟头。牛客判题用例经常故意给s = "I am a programmer"(中间有两个空格),你用单空格切割会出现空字符串元素,最后输出多出一堆空格,判题直接不给过。

我当时第一次提交就是这个问题,后来养成了一个习惯:凡是字符串切分,先确认题目说的是“空格分隔”还是“单个空格分隔”。描述模糊时,优先用split()(无参数),它会自动按任意空白字符切分并过滤空串。这道题用无参split()就能干净解决。

代码实现很简单:

s = input().strip() words = s.split() result = " ".join(words[::-1]) print(result)

这里用strip()去掉首尾多余空格,再用无参split()切出单词列表,最后倒序拼接输出。整段代码不到五行。但注意,Python的split()默认处理空白字符,包括空格、Tab和换行,这正好应对了题目的各种隐藏空格陷阱。

2.2 题目二:统计字符串中的数字并求和

这题也很典型:给你一个包含字母和数字的混合字符串,需要提取出里面所有连续的数字子串,并把它们加起来。例如输入"abc123def45gh6",输出174(123+45+6)。

这题考的是对字符串遍历的敏感度。常见做法是维护一个“当前累积数字”的变量cur,遇到数字字符就用cur = cur * 10 + int(ch),遇到非数字字符就把cur加到总和并清零。循环结束后还要再判断一次cur是否为0,否则末尾的数字会漏加。这一步非常关键,经常有人在这里丢分。

我自己写的时候,为了更稳,直接用正则表达式:

import re s = input().strip() numbers = re.findall(r"\d+", s) total = sum(int(num) for num in numbers) print(total)

re.findall(r"\d+", s)会把所有连续数字子串都找出来,然后逐个转成整数求和。正则的优点是省心,不容易漏边界;缺点是有的人对正则不熟,面试时被问到底层实现会答不上来。所以我建议两种方案都掌握:笔试追求速度用正则,平时练习建议手写遍历,这样你对字符处理的敏感度才会真正提升。

2.3 字符串类题目的细节避坑指南

字符串题目看起来简单,实际上有很多常见的坑,我在这套题上踩过的、帮别人排查过的,整理成了一张速查表,你可以直接照着自查。

坑点错误示例正确做法
切分后出现空字符串s.split(" ")遇到连续空格使用无参split()
末尾数字漏加遍历循环结束后没处理cur循环后再判断一次cur
大小写干扰统计字母时没统一大小写lower()转换再处理
输入含首尾空格直接对原字符串操作strip()去掉首尾空白

这些细节看着小,但笔试现场就是靠这些拉开差距。同样的思路,别人AC了,你卡在0%,区别往往不在算法,而在这些“脏活”处理上。我见过太多人因为字符串末尾多了一个换行符,比对结果死活不对,最后发现是input()strip()。这种时间浪费完全可以避免。

3. 数组、查找与数学规律题的解法思路

3.1 题目三:找出数组中唯一出现一次的数字

这道题描述很经典:给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次,找出那个只出现一次的元素。要求算法尽量高效,不额外开辟大空间。

很多人的第一反应是用哈希表统计频率,再找出值为1的键。这种做法能过,但被问到“你能不能不用额外空间”时就会卡住。这道题的最优解是异或运算:相同的数字异或为0,任何数字和0异或还是它本身,所以把数组中所有数字依次异或一遍,出现两次的都会抵消,剩下的就是那个只出现一次的数字。

我第一次看到这个解法时觉得太巧妙了,后来做题多了才发现,异或处理“成对抵消”是笔试里的高频套路。代码写出来异常简洁:

nums = list(map(int, input().split())) result = 0 for num in nums: result ^= num print(result)

这里只有一个循环,时间复杂度O(n),空间复杂度O(1)。我能理解为什么笔试爱考这种题——它考察的不仅是你会不会写哈希表,而是你对位运算有没有概念。如果你只会暴力解法,很多后续题目都会做得非常吃力。

3.2 题目四:约瑟夫环的模拟实现

约瑟夫环是这套题里少有的“模拟”题,也是最容易出现“看着会做,一写就错”的题。题目描述通常是:n个人围成一圈,从第1个人开始报数,报到m的人出圈,然后从下一个人重新报数,问最后剩下的人的原始编号是多少。

最直观的解法是模拟整个过程:用列表存所有人,用指针移动,每报到m就弹出一个人。Python里用列表模拟这个过程,代码比较短,但要注意索引的计算。每弹出一个元素后,列表长度减一,指针要相应回退,否则会跳过或重复处理元素。

这里我直接给两种写法。第一种是用列表模拟,适合数据量小的情况:

n, m = map(int, input().split()) people = list(range(1, n + 1)) idx = 0 while len(people) > 1: idx = (idx + m - 1) % len(people) people.pop(idx) print(people[0])

第二种是数学递推法,不使用列表,直接通过状态转移得出最后编号:

n, m = map(int, input().split()) res = 0 for i in range(2, n + 1): res = (res + m) % i print(res + 1)

第二种解法代码更短,但需要你理解约瑟夫环的递推公式。笔试时如果数据范围小,用第一种模拟就够了;如果n高达十的六次方甚至更大,就必须用递推法,否则会超时。这道题能不能拿满分,取决于你能不能判断出当前数据范围该选哪种方案。

3.3 数组边界与特殊输入的处理经验

数组类题目最常见的错误来源是索引越界。这套题里我遇到过n=1甚至n=0的边界用例。有些同学一上来就写if nums[1] > nums[0],数据长度为1时直接崩掉。我的习惯是:拿到数组题,先问自己三个问题——数组为空怎么办?数组长度为1怎么办?最大值和最小值相等怎么办?这三个问题想清楚,再动手写代码,能减少一大半的错误提交。

还有一个经验是:在牛客这种ACM模式下,数组输入可能是同一行,也可能是多行,题目描述会说清楚。如果没把握,最好用sys.stdin.read()一次性读取全部数据,再拆分解析,这样无论换行方式怎么变都不会出错。下面是一个通用读取模板:

import sys data = list(map(int, sys.stdin.read().split()))

这种读法在处理多组输入时尤其好用,它可以忽略所有空格和换行,直接把整份输入转成整数列表,自己按需切片。使用这个模板后,我再也没有因为“换行符导致读取出错”这类问题浪费过时间。

4. 动态规划题型的识别与应对

4.1 题目五:爬楼梯的经典解法与空间优化

爬楼梯属于几乎每套笔试题都会出现的入门动态规划题。题目很直观:你正在爬楼梯,需要n阶才能到顶,每次可以爬1阶或2阶,问有多少种不同的方法爬到楼顶。

这道题最直接的思路是递归,但纯递归会重复计算大量子问题,n稍大就直接超时。正确做法是动态规划:定义 dp[i] 表示爬到第i阶的方法数,那么第i阶只能从第i-1阶跨一步,或者从第i-2阶跨两步到达,所以状态转移方程为 dp[i] = dp[i-1] + dp[i-2]。初始条件 dp[1]=1,dp[2]=2。

按照这个思路,代码很清晰:

n = int(input()) if n <= 2: print(n) else: a, b = 1, 2 for _ in range(3, n + 1): a, b = b, a + b print(b)

这里我没有开整个dp数组,而是用两个变量滚动更新,把空间复杂度从O(n)降到O(1)。很多刷题指南都会强调这种优化,因为当n达到十万甚至百万时,开数组的写法和滚动更新的写法,内存占用完全是两个量级。

4.2 题目六:最长连续上升子序列的遍历技巧

这题在整套题里属于“看着像动态规划,其实普通遍历就能解决”的题型。题目问的是:给定一个整数数组,找到其中最长连续上升子序列的长度。注意关键词是“连续”,这意味着你不需要回溯重新选择。

我见过不少同学一看到“上升子序列”就想上最长递增子序列的动态规划模板,dp数组开了、两层循环写了,结果数据一大就超时。其实“连续”这两个字已经把难度降了很多:你只需要遍历数组一遍,如果当前元素比前一个元素大,当前长度加一;否则从1重新开始计数,同时更新最大长度。

nums = list(map(int, input().split())) if not nums: print(0) else: max_len = 1 cur_len = 1 for i in range(1, len(nums)): if nums[i] > nums[i-1]: cur_len += 1 max_len = max(max_len, cur_len) else: cur_len = 1 print(max_len)

这题之所以值得讲,是因为它提醒你做题前一定要先读清楚题干。是“连续”还是“非连续”,解法复杂度天差地别。如果你一上来就套模板,不光代码复杂,还可能超时。笔试现场时间有限,看清题目条件再动手,比盲目刷题重要得多。

4.3 从零开始搭建DP思路的思考路径

很多人对动态规划有种莫名的恐惧,一看到“DP”两个字就觉得自己不行。这套一模题里的DP其实都特别基础,刚好适合用来建立正确的思考路径。我的经验是分四步走:第一步,明确状态,想清楚 dp[i] 代表什么;第二步,找转移方程,想清楚当前状态能从哪些状态推导出来;第三步,定初始条件,把最小规模的解直接写死;第四步,确认遍历顺序,是从小到大还是从大到小。

如果你做一道题想不过来,建议先在纸上画出dp数组的变化过程。比如爬楼梯那题,你手写出 dp[1]=1, dp[2]=2, dp[3]=3, dp[4]=5,看到斐波那契数列的规律后,自然就理解了为什么要用前两项求和。我在学习DP的那个阶段,靠的就是这种“笨办法”——不急着写代码,先手动算几个例子,公式就藏在这些例子里面。

5. 笔试现场的时间分配与问题排查

5.1 做题顺序建议:先捡软柿子捏

整套题做下来,我的强烈建议是:不要按题号顺序硬刚。先把所有题都快速扫一遍,标记出哪些是前几分钟就能拿下的送分题,哪些是需要推理的模拟题,哪些是DP题。然后按“送分题 -> 模拟题 -> DP题”的顺序做。这样做的目的很现实:先把能拿的分稳稳拿住,再留大块时间啃硬骨头。

有些同学喜欢从第一题按顺序做到最后一题,一旦前面卡住了,后面简单题也没时间做。这种策略在笔试里非常不划算。牛客判题是按通过率给分的,哪怕你最后一道大题的用例通过一半,也比前面一道题都做不出来强。我自己的节奏是:总共120分钟,前40分钟搞定三道送分题,中间60分钟攻模拟题和中等题,最后20分钟留给DP尝试。如果你基础稍弱,可以适当调整比例,但“先做能拿分的”这个大原则不要变。

5.2 常见运行错误与排查技巧速查表

笔试过程中最烦的不是“题目不会做”,而是“程序莫名其妙报错”。我把这套一模题里最容易出现的问题类型整理了一下,你在本地测试时可以直接对着看:

错误类型可能原因排查方向
IndexError列表越界检查循环边界,尤其是n=1的边界
Time Limit Exceeded循环嵌套过深是否有O(n²)的暴力解可以优化
NumberFormatError输入包含非数字内容检查split()后是否需要过滤空串
Wrong Answer逻辑对但边界漏判针对输入最小值、最大值、空值测试
MemoryError开了过大的数组改为滚动变量或减少存储维度

这套表是我当年刷完题后自己总结的,之后每次笔试前都会扫一眼,比临时翻笔记管用得多。尤其是“Wrong Answer但实在查不出错”的情况,九成都是边界条件没覆盖到。这时候不要干瞪眼,试着往输入里塞几个极端值,比如空字符串、0、负数、超大数,问题通常马上浮出水面。

5.3 实测有效的查错小习惯

我分享一个自己的笨办法,虽然土,但救了我很多次:写完代码后,先不要立刻提交,自己手动在注释里列出三组测试用例,分别是普通输入、边界输入、极端输入,然后逐组推演一遍结果。比如做字符串反转那题,我就在草稿纸上写“输入空字符串”“输入全是空格”“输入只有一个单词”,然后确认代码输出都符合预期,再放心提交。

这个方法看起来多花了几分钟,但它能大幅减少“提交一次错一次”的循环。牛客笔试有的场次提交错误会有罚时,反复提交错误答案,成绩会受到很大影响。与其赌运气,不如提交前自己把雷扫一遍。还有一个小细节:写完代码后整体读一遍,重点看if/else的条件分支是否可能互相覆盖,while循环会不会死循环,数据范围大的时候int是否够用。这些检查加起来不到五分钟,却能避开很多常见失分点。

5.4 把模考题变成自己的题库

这套一模题最值得借鉴的价值,不是题目本身,而是它揭示的考点分布规律。你做完之后,完全可以按同样的思路去整理自己的错题库:每道题记录四栏——题目类别、我的错误原因、正确解法、同类题扩展。比如约瑟夫环这道题,你可以把解法整理成“模拟法 + 递推法”的对比笔记,再去找其他“报数出圈”类题目做两遍,直到闭着眼都能写出来为止。

我自己的习惯是每套模考做完后,都会把代码按专题存到一个文件夹里,并标记做题日期和AC状态。两个月后再重新打开这些文件夹,看自己当时写的代码,能非常直观地感受到进步。这种积累方式比刷完就忘有效得多。刷题不是目的,能把每道题背后的思考方式消化成自己的东西,才是做这套一模编程题集合的真正意义。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/9 6:25:02

多模态CLAP模型:音频-文本对比学习与检索实战

从论文标题来看&#xff0c;这明显是一篇学术论文的内容&#xff0c;但实际上我可以把它转化为一篇技术教程向的CSDN博文&#xff0c;重点聚焦于多模态CLAP模型的原理、训练方法、微调实战与工程落地。我会以这个方向来写。之前在做音视频多模态检索项目时&#xff0c;经常遇到…

作者头像 李华
网站建设 2026/9/6 3:36:57

基于YOLOv8的景区救生衣穿戴监测系统完整实战

简介&#xff1a;本资源是一套面向计算机、人工智能及相关专业在校学生与初学者的毕业设计级项目——基于YOLOv8的景区游船救生衣穿戴智能监测系统&#xff0c;聚焦真实安防场景下的目标检测应用&#xff0c;解决水上旅游安全监管中人工巡检效率低、漏检率高等问题。压缩包共8个…

作者头像 李华
网站建设 2026/9/5 23:19:09

大疆Air 2对比Air 2S:传感器升级值不值?选购指南

这次我们不聊模型部署&#xff0c;也不聊接口调用&#xff0c;而是聊一台很经典的消费级无人机&#xff1a;大疆 Air 2 和 Air 2S。这两台机器在二手市场和入门进阶用户里讨论度一直很高&#xff0c;核心问题就一个&#xff1a;Air 2S 比 Air 2 贵出来的那部分钱&#xff0c;到…

作者头像 李华
网站建设 2026/9/6 4:59:11

SpringBoot住宅小区物业管理系统:从JWT认证到安全部署全解析

简介&#xff1a;本资源是一套基于SpringBoot框架开发的住宅小区物业管理系统完整源码&#xff0c;面向Java初学者、Web全栈学习者及物业信息化项目实践者&#xff0c;旨在解决传统小区管理中缴费难、报修慢、公告滞后等痛点&#xff0c;提供可运行、可二次开发的企业级应用参考…

作者头像 李华
网站建设 2026/9/5 21:25:55

双边拉普拉斯变换收敛域:从极点到信号类型一次理清

双边拉普拉斯变换的收敛域问题&#xff0c;是很多通信考研同学在复习“信号与系统”时最容易卡住的地方。明明正变换公式背得很熟&#xff0c;但一做题碰到收敛域判断就懵&#xff1a;为什么有的题收敛域是 (\sigma > a)&#xff0c;有的却是 (\sigma < a)&#xff1f;为…

作者头像 李华
网站建设 2026/9/4 16:27:23

墙体裂缝图像分割数据集实战:基于YOLOv8/v11的训练与部署指南

简介&#xff1a;本资源是专为计算机视觉开发者与建筑安全检测研究者设计的墙体裂缝图像分割数据集&#xff0c;适用于YOLOv8、YOLOv11等主流目标检测与实例分割模型的训练与验证&#xff0c;解决建筑表观缺陷自动化识别中的标注数据匮乏问题。压缩包共488个文件&#xff08;19…

作者头像 李华