GESP五级首题栈解法:混合括号序列的合法性与最大嵌套深度

发布时间:2026/10/21 15:42:20
GESP五级首题栈解法:混合括号序列的合法性与最大嵌套深度 1. 五级首题为什么考了一道“数据结构热身题”GESP2026年C五级编程题第一题在考后讨论里热度比想象中高。原以为是道送分题结果不少考生反馈“样例过了交上去却拿不到满分”问题几乎都出在边界处理上。这其实非常符合五级首题的定位它不是压轴难题不需要动态规划也不涉及图论但恰好能把“读题是否仔细、基础数据结构是否熟练、边界意识是否到位”一次全部检验出来。五级的难度介于四级和六级之间。四级还在培养基本的枚举、模拟、简单排序能力六级开始真正接触树、图、动态规划的入门模型。而五级处在中间的衔接带最重要的任务就是建立“用标准数据结构去解决规则问题”的思维。第一题承担的是稳定心态的职责不可能一上来就是复杂算法所以命题人最喜欢做的就是从三类模板里选一种考栈括号匹配、表达式求值的简化版、字典表字符串词频统计、排名去重、队列或双端队列固定规则的模拟流程。2026年这道题走的就是“字符串 栈”的路线。放在2026年这个时间点看这个选择一点都不意外栈是五级大纲里的核心数据结构括号匹配又是栈最经典、最没有争议的应用场景。代码量可以控制在几十行以内又能自然延伸出嵌套深度、合法性判断、非法序列输出等多个考点非常适合作为全卷第一题给认真准备过的学生送分给靠蒙或死记模板的学生一记响亮的提醒。我在辅导中反复和学生说这种题的训练价值不在“会不会写”而在“能不能一次写对”。很多同学看到括号匹配就觉得简单结果不是忘了判空栈就是没仔细处理右括号与左括号的类型对应关系又或者在算最大深度时更新时机错了。下面我以这道题的核心考法——混合括号序列的合法性与最大嵌套深度——作为原型完整还原从审题到AC代码再到自测的全过程。这套思路可以直接套用到绝大多数基于栈的字符串处理题上。2. 题目原型括号序列到底要输出什么2.1 题目描述的还原版本根据考生回忆和命题风格整理这道题的大意如下输入一行只包含六种字符的字符串 s字符分别是( ) [ ] { }长度满足 1 ≤ |s| ≤ 10^5。如果字符串是一个合法的括号序列输出它的最大嵌套深度如果不是合法的括号序列输出 -1。这里需要解释两个关键概念因为它们直接决定算法怎么写。合法括号序列每个右括号都要和它“左侧最近、尚未闭合、且类型相同”的左括号配对。左括号必须先出现的后闭合也就是遵循栈的先进后出规则。像([{}])这种层层嵌套的序列是合法的而([)]虽然左右括号数量完全相等却因为类型交叉配对不合法。最大嵌套深度在扫描过程中同一时刻连续嵌套且尚未闭合的左括号数量就是当前深度整个过程中出现的最大深度就是答案。([{}])的深度会一路从1加到3所以输出3。2.2 这道题真正在考察什么表面上看考的是栈实际上考的是两个子问题的组合合法性判定类型匹配加顺序匹配二者缺一不可。深度计算在合法性成立的前提下深度就等价于栈能到达的最大高度。这两个子问题被巧妙地糅在了一道题里如果你只写一个计数器去统计括号数量那你能拿到“判断左右数量相等”这部分思路分但会挂在([)]这种样例上如果你只知道用栈判断合法性却不知道在压栈过程中顺手记录最大深度又会漏掉第二个子问题的分数。所以正确的思考顺序是先把“合法性”和“深度”当成两个独立的输出信号再去想怎么用一个栈同时完成。千万不要一上来就想“我要输出最大深度”于是只维护一个整数计数器那样会把类型匹配信息丢掉。栈在这里不是可选项而是必须项因为它保存的是完整的、按顺序排列的未闭合左括号序列。2.3 一个手把手的推演拿样例([{}])来说扫描过程是这样的遇到(入栈当前深度1最大深度更新为1。遇到[入栈当前深度2最大深度更新为2。遇到{入栈当前深度3最大深度更新为3。遇到}栈顶是{类型匹配弹出当前深度回到2。遇到]栈顶是[类型匹配弹出当前深度回到1。遇到)栈顶是(类型匹配弹出当前深度回到0。扫描结束栈为空序列合法输出最大深度3。再看非法的([)]遇到(入栈深度1。遇到[入栈深度2。遇到)此时栈顶是[但右括号)期望匹配的是(类型不匹配直接输出-1。这个推演过程看起来简单但建议每个人都在草稿纸上亲手模拟一遍。我在实际教学里发现能完整写出这个推演过程的学生代码基本不会出原则性错误反过来跳过推演直接开写的人往往会在某个角落漏掉判断。3. 解题主线为什么非用栈不可以及三个终止条件3.1 用计数器为什么不行初学者最容易提出的方案是遇到左括号计数加一遇到右括号计数减一如果最终计数为零就认为合法过程中记录最大值就是答案。这个思路对同类括号((()))是有效的但一旦引入多类型括号立刻失效。问题出在“类型对应关系”上。计数器的状态只是一个数字它不可能记住“目前有一个[还没闭合它的上面还有一个(”。当遇到)时计数器只知道当前未闭合左括号总数是2却不知道栈顶到底是(还是[。所以([)]这种左右数量完全成对、但顺序错误的序列计数器方案会错误地判断为合法。栈的价值就在于它保存的不只是数量而是完整的未闭合左括号序列。栈顶就是“最近的、还没闭合的左括号”右括号到达时只需要看栈顶一个元素就能完成类型匹配判断。这个特性完美对应题目中“左侧最近且尚未闭合”的定义。3.2 主算法流程整个算法用一段伪代码就能说清楚dep 0 maxDep 0 stack st for ch in s: if ch 是左括号: st.push(ch) dep dep 1 maxDep max(maxDep, dep) else: if st 为空: 输出 -1结束 if st.top() 不是与 ch 匹配的左括号: 输出 -1结束 st.pop() dep dep - 1 if st 非空: 输出 -1 else: 输出 maxDep这段伪代码里有三个终止条件任何一个触发都说明序列非法直接输出-1遇到右括号时栈为空说明当前右括号没有可匹配的左括号典型的例子就是))(。遇到右括号时栈顶类型不匹配说明交叉配对典型的例子就是([)]。扫描结束后栈非空说明存在没有被闭合的左括号典型的例子就是((()。这三个条件顺序一个都不能调换。先判空再判类型是因为空栈时根本不存在栈顶元素先判类型再判空则会在空栈时访问不存在的栈顶造成运行时错误。3.3 最大深度为什么要在入栈时更新很多同学会疑惑为什么maxDep的更新放在入栈分支而不是出栈分支道理很简单当前深度是由“未闭合的左括号数量”决定的而这个数量只会在左括号出现时增加。每压入一个左括号就说明嵌套层数爬高了一层。出栈只是下降过程不可能创造新的最大深度。如果你只在出栈时更新maxDep那么像([{}])这种序列出栈时深度分别是从3降到2、从2降到1、从1降到0取到的最大值是2正确答案3就被漏掉了。这个细节属于“代码结构正确但业务逻辑错误”的典型问题样例可能能过但换一组数据就露馅。4. 完整C实现与逐段注解4.1 考场可用的AC版本下面这版代码是我在考场环境下会直接提交的版本追求的是“短、清晰、不容易写错”#include bits/stdc.h using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); string s; cin s; unordered_mapchar, char match { {), (}, {], [}, {}, {} }; stackchar st; int dep 0; int maxDep 0; for (char ch : s) { if (ch ( || ch [ || ch {) { st.push(ch); dep; if (dep maxDep) { maxDep dep; } } else { if (st.empty()) { cout -1 \n; return 0; } if (st.top() ! match[ch]) { cout -1 \n; return 0; } st.pop(); dep--; } } if (!st.empty()) { cout -1 \n; return 0; } cout maxDep \n; return 0; }4.2 关键写法说明头文件与万能头#include bits/stdc.h是竞赛编程里常用的万能头文件官方编译环境通常支持。如果你们本地的练习环境不支持可以拆开写成#include iostream、#include stack、#include unordered_map、#include string效果完全一样。类型匹配的映射表我用unordered_map把右括号映射到对应的左括号这样主循环里只需要一句st.top() ! match[ch]就能完成类型判断。如果不喜欢这种写法也可以写成函数char getMatch(char ch) { if (ch )) return (; if (ch ]) return [; if (ch }) return {; return \0; }两种方式等价但映射表在代码量更大的题目里更容易扩展。比如未来要加一种新的括号类型映射表只需要加一行而if-else链会越写越长。输入输出同步关闭ios::sync_with_stdio(false); cin.tie(nullptr);这两行在往年GESP里不一定非写不可因为第一题的字符串长度只有10^5。但从养成习惯的角度我建议每道题都写。它能让cin/cout的输入输出速度快出不少避免在数据量稍大的测试点出现不必要的超时。关于match[ch]的安全性当ch是右括号时match里一定有对应的键不会插入新键。题目保证字符串只包含六种字符所以不用担心中间混入空格或其它符号。4.3 一个可维护性更好的函数化版本如果你还在上机练习阶段我更推荐把核心逻辑封装成函数这样以后遇到类似题目可以直接复用bool check(const string s, int maxDep) { stackchar st; unordered_mapchar, char match { {), (}, {], [}, {}, {} }; int dep 0; maxDep 0; for (char ch : s) { if (ch ( || ch [ || ch {) { st.push(ch); dep; maxDep max(maxDep, dep); } else { if (st.empty() || st.top() ! match[ch]) { return false; } st.pop(); dep--; } } return st.empty(); }main里只需要调用函数根据布尔结果输出即可。这种结构的好处是职责单一主程序只管输入输出合法性判断与深度计算全部封装在内。考试时时间紧张可能没必要再封装一层但平时训练建议这么做因为五级之后的题目复杂度会明显上升尽早培养“函数拆分逻辑”的意识后面学递归、学分治都会更顺。5. 测试用例设计从样例到极端边界一个都不能少5.1 我自己一定会跑的一组用例这道题样例通常只有两三组覆盖面非常有限。我自己在上考场前会把下面这组用例背下来每写一道匹配题都会拿它们快速自测输入预期输出说明()1最基础的一层嵌套([{}])3不同类型逐层嵌套最典型的合法样例([)]-1左右数量相等但类型交叉最容易漏判(((())))4同一类型多层嵌套((((((()))))))7嵌套深度较大验证最大深度统计7个左括号7个右括号((()-1结束后栈不为空左括号未闭合)))-1一开始就遇到右括号需要判空栈(){[]}2合法但深度先降后升验证最大深度不会被低估{}([]){}2多段交替匹配检查栈能否正确清空这里特别提一下(){[]}这个用例它的深度变化是1→2→1→2→1真正的最大值出现在第二段[]处。如果代码只在入栈时更新maxDep这个用例恰好能验证“最大深度是否会被中间出栈操作影响”。如果只在出栈时更新它也会暴露错误。5.2 边界用例为什么能救命字符串长度上限是10^5所以理论上最大深度不会超过10^5用int存储完全够用不需要long long。真正的风险不在于整数溢出而在于逻辑边界空栈出栈)))这种输入会让没写判空代码的程序崩溃。在 C 里对空栈调用top()或pop()属于未定义行为轻则输出错误重则直接报运行时错误。结束后栈有残留((()这种输入如果不做最终判空程序会输出一个正数但实际上应该输出-1。最大深度出现在最后((((((()))))))这种输入只有入栈时更新maxDep才能正确得到7。这些边界用例的价值在于它们能把“代码能跑”和“代码正确”区分开。我在初学阶段也吃过亏样例全过交上去错了一大半就是因为测试用例没覆盖到这些极端情况。6. 考场经验这道题真正在淘汰什么样的人6.1 高频丢分点清单考完复盘时我听到最多的丢分原因集中在这几个地方没判空栈直接pop。这是最严重的运行时错误一出现整个测试点就是0分。只统计括号数量不记录类型。样例可能是(()())之类的纯圆括号样例代码在样例上没问题一到混合类型就挂。最大深度只在出栈时更新。最终答案永远比正确答案小1到若干输出结果完全错误。扫描结束后忘记检查栈是否为空。遇到只有左括号的测试点输出错误的正数。使用字符数组时空间开小了。有的同学还在用char s[100]接长度10^5的输入直接越界。很多人觉得括号匹配“太简单不用专门练”但实际上它考察的就是基本功扎不扎实。一道如此简单的题区分度却很高基础扎实的同学五分钟AC靠背模板的同学可能卡二十分钟还查不出错在哪。6.2 考场上的提交顺序建议我的建议是拿到试卷先花两分钟通读全部题目然后回到第一题按下面的顺序推进读题两分钟圈出输入规模和输出条件特别是“非法时输出-1”这种说明。手推一个例子拿样例的合法序列和非合法序列各推一遍把栈的变化过程写在草稿纸上。写代码按照“入栈分支-出栈分支-最终判空”的三段结构写写完后对照伪代码检查一遍。跑自测用例至少跑三组——一组合法嵌套、一组类型交叉非法、一组单边括号非法。这套流程加起来的耗时熟练以后大约十到十五分钟。不要小看这十五分钟它能决定你整个卷面的心态。第一题稳了后面就算遇到难题心里也不会慌。6.3 给第一次参加五级的同学一句提醒不要在考试时临时创造“更聪明的写法”。栈是这道题的标准答案也是经过无数人验证的最可靠方案。考场上最怕的就是觉得“用栈太简单我是不是应该写个递归”然后在递归里System崩溃。第一题要的是正确的、可控的、不给自己找麻烦的解法越朴素越好。至于优雅和高性能那是保证正确之后才有资格追求的东西。7. 进阶如果第一题变成“多次区间查询”思路怎么升级最后留一个扩展思考给那些觉得第一题太基础、想继续冲六级和七级的同学。假如题目加一个条件给定 m 次询问每次给一个区间 [l, r]问这个子串是否合法、最大深度是多少。直接对每个询问都扫描一遍子串复杂度是 O(mn)在 n 和 m 都到10^5时会超时。这类问题的经典切入思路有两个方向前缀和思想把左括号视为1右括号视为-1那么一个区间要合法首先整体和为0其次任意前缀和不能变成负数。配合预处理前缀和数组以及区间最小值查询可以快速判断合法性。线段树或树状数组每个位置可以抽象成一个权重值区间最大深度可以转化为维护最大前缀和与最小前缀和的组合运算。线段树节点存储区间前缀和的最小值最大值就能在 O(log n) 时间内回答一次询问。这个方向就不再展开了因为它已经超出五级第一题的考纲范围。但我想强调一点括号匹配并不是一道孤立的小题它背后隐藏着“前缀和模型”“区间最值查询”“线段树信息合并”这一整条升级路线。五级把它作为第一题是一个非常好的“地基题”。把这套栈思路彻底吃透后面学习更复杂的区间数据结构时你会发现很多概念都能连接到今天这道题上。按我自己刷题的经验把一道简单题做到“不丢分、完全掌握、还能勾连出扩展方向”比囫囵吞枣刷十道难题更有价值。2026年这道五级首题就是这种值得多花一点时间的题。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询