尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

最长有效括号三种解法:栈、动态规划与O(1)双向计数

发布时间:2026/9/26 13:03:03

资讯中心
01
ARTICLE

最长有效括号三种解法:栈、动态规划与O(1)双向计数

最长有效括号三种解法:栈、动态规划与O(1)双向计数
最长有效括号力扣第32题在热题100里属于那种一眼看上去很基础、上手一写就翻车的题目。题目描述只有一句话给定一个只包含(和)的字符串返回最长有效括号子串的长度。所谓的有效括号子串要求格式正确且连续比如(()里最长的是()答案是2)()())里最长的是()()答案是4。这道题真正难的地方在于它不是在数括号数量够不够而是要在一段连续子串上做严格匹配而且经典的解法能一路从暴力、栈、动态规划写到 O(1) 空间的双向计数几乎把算法面试里最常见的几类思维模式全串起来了。这篇文章我会用 Java 把三种主流解法完整走一遍栈解法、动态规划解法、双向计数解法。每种解法都会说清楚为什么这么设计、代码每一行为什么要这样写、复杂度怎么算以及我在实际刷题和面试复盘里踩过的坑。不管你是刚开始刷力扣热题100的新手还是准备 Java 后端面试、想把手上的题解体系化整理的老手这篇都能给你一份可以直接复用的思路框架。1. 问题本质与暴力思路拆解为什么不能直接数括号数量1.1 先吃透有效括号子串这个定义很多初学者拿到这道题第一反应是数左右括号的个数觉得只要左括号等于右括号就是有效。这个直觉在判断整个字符串是否是合法括号序列时是成立的但放在最长有效子串这道题里就完全不够了因为题目里有两个限定词有效和连续。先看有效。一个括号子串有效意味着任意前缀中右括号的数量不能超过左括号并且最终左右数量相等。换句话说这是一对对括号的正确嵌套像()(())、(()())、((()))都是有效串而())(()、(()))(都不是。这个任意前缀右括号不超左括号的条件是后面所有解法里判断是否该重置的根因。再看连续。子串要求索引连续位置必须挨着不能跳过中间的非法字符。这一点让题目从计数变成了在连续区间里找合法片段。比如)()())这个字符串整体不是一个有效括号串但它包含连续的有效片段()()长度是4。我们要找的就是这种夹在无效字符之间的合法片段而不是把字符串里散落的括号凑起来。有些变体题会问最长有效括号子序列那个允许跳着选答案直接用双计数器就能算出来但热题100这道题明确是子串必须连续难度一下子就上来了。还有一个常见的理解偏差())里最长有效子串是()长度为2不是把())当成一个整体去看。也就是说我们要在所有可能的连续子串里挑出长度最大的那一个合法片段。这个片段概念贯穿始终栈解法里的栈底元素、动态规划里的dp[i]、双向计数里的计数器重置本质上都是在追踪当前合法片段的边界在哪里。1.2 暴力解法与 O(n²) 的改良写法暴力思路是最直观的枚举所有起点和终点切出所有子串逐个检查是否是有效括号串取最大长度。枚举子串本身是 O(n²)每次检查又要从头扫一遍做括号匹配判断整体就是 O(n³)。n 小时无所谓但力扣上这道题的数据范围是字符串长度最大 3×10⁴O(n³) 直接超时没有任何优化余地。暴力也能改得聪明一点降成 O(n²)固定起点不断向右扩展终点同时维护left和right两个计数器。遇到(就 left遇到)就 right。如果某个时刻 right 大于 left说明以当前起点到这里的这段子串已经不可能再成为有效括号串了因为右括号多了直接 break 掉换下一个起点。如果 left 等于 right说明当前这段是有效的记录长度。这样每个起点最多扫一遍总体是 O(n²)。for (int i 0; i n; i) { int left 0, right 0; for (int j i; j n; j) { if (s.charAt(j) () left; else right; if (right left) break; if (left right) max Math.max(max, left right); } }这个 O(n²) 版本虽然是过渡方案但它在思维上非常重要它揭示了合法片段中断的唯一原因是右括号数量超过了左括号这一规律。后面三种优化解法本质上都是在用不同的数据结构或策略去高效处理这个超越即失效的边界。我建议你写代码前先在纸上跑一遍这个暴力版本把(()和)()())两个例子都走一遍你就知道答案的 2 和 4 到底是从哪里冒出来的了。2. 栈解法用下标而不是括号本身求最长有效括号2.1 为什么栈里要存索引而不是括号字符栈是处理括号匹配问题的天然工具因为括号的最近匹配特性和栈的 LIFO 特性完全一致遇到左括号压栈遇到右括号弹栈弹出去的左括号就是最近一个还没匹配的左括号。但在这道题里栈里存的不是字符而是括号在字符串中的索引下标。原因很简单题目要的是长度而长度必须通过下标相减得到。如果栈里只存括号字符(那么弹栈之后我们根本不知道这个左括号在字符串的哪个位置也就没法计算子串长度。存下标之后当右括号弹掉一个左括号下标j时以这个右括号结尾的有效子串长度可以直接用i - j 1表示吗其实不行因为弹出之后栈里还有更早的左括号或者边界标记真正的答案是i - stack.peek()。这就引出了栈解法最精髓的一步初始化时先压入一个 -1。这个 -1 是一个哨兵代表当前合法片段的起点前一个位置。当遇到右括号并且弹出的是哨兵时栈空了说明这个右括号没有匹配对象它是一个断裂点我们需要把当前下标 i 压入栈作为新的哨兵也就是新的合法片段起点前一位。举个具体例子字符串()索引0是左括号压栈栈变成[-1, 0]索引1是右括号弹栈弹出0此时栈顶是哨兵 -1长度就是1 - (-1) 2刚好是整个子串的长度。如果没有哨兵 -1这个长度就算不出来因为栈只有一个元素被弹掉了栈空之后没有参照位置。这就是哨兵的意义它始终指向当前扫描到的合法片段的最左边界。2.2 栈解法 Java 代码与手工推演下面是完整的 Java 实现我用的是ArrayDeque作为栈注意它不是线程安全的但单线程刷题完全够用而且性能比Stack类更好Stack本身继承自Vector有同步开销力扣上用ArrayDeque是主流选择。class Solution { public int longestValidParentheses(String s) { DequeInteger stack new ArrayDeque(); stack.push(-1); int max 0; for (int i 0; i s.length(); i) { char c s.charAt(i); if (c () { stack.push(i); } else { stack.pop(); if (stack.isEmpty()) { stack.push(i); } else { max Math.max(max, i - stack.peek()); } } } return max; } }这个解法的时间复杂度是 O(n)每个字符最多入栈出栈一次空间复杂度是 O(n)栈最多压入 n 个下标。逻辑上只有四个分支非常简洁但越是简洁的代码越容易在细节上出错。我们用手工推演跑一遍)()())看看长度4是怎么得出来的i0字符)弹出栈顶的 -1栈空把0压入栈。栈[0]。此时0作为新的断裂点。i1字符(压栈。栈[0, 1]。i2字符)弹出1栈顶是0长度2-02max 更新为2。栈[0]。i3字符(压栈。栈[0, 3]。i4字符)弹出3栈顶是0长度4-04max 更新为4。栈[0]。i5字符)弹出0栈空把5压入栈。max 仍是4。最终答案4完美对应子串()()的长度。注意这里最关键的一点i2 的时候我们并没有从0这个断裂点重新开始计数而是通过i - stack.peek()直接跨过断裂点之前的所有内容。因为断裂点0本身是无效字符它不参与任何合法片段所以它只作为边界参照存在。这种用栈底元素记录边界的思路正是这道题和普通括号匹配题最大的区别。2.3 栈解法最容易写错的三个点第一忘记初始化 -1。如果你上来就stack.push(0)并且循环从1开始边界处理会变得非常别扭一旦遇到首字符是右括号的情况栈就会出问题。统一在循环前压入 -1让它作为虚拟边界代码会干净得多所有情况都能统一处理。第二用StackCharacter存字符而不是存下标。这是新手最容易犯的错误存字符的话等到要计算长度时才发现根本没有位置信息只能临时改代码。宁可先想清楚再动手刷题时把长度由下标差决定这个意识刻在脑子里。第三右括号弹出后栈空的情况处理。很多人会把if (stack.isEmpty()) { stack.push(i); }这一步漏掉觉得栈空了就让 max 保持原样就好。但如果不把当前右括号下标压栈作为新的断裂点后面合法的()子串就无法正确定位起点。比如)()()这个例子如果漏掉这一步后面的长度计算基准会乱掉答案会偏大或偏小。这一步绝不是可选的它是栈解法保持正确性的关键。3. 动态规划解法以 dp[i] 为结尾的状态推导细节3.1 dp 数组的状态定义与两类转移方程动态规划解法在很多题解里被列为基础解法但实际面试中能把转移方程讲明白的人不多。我们先定义状态dp[i]表示以索引 i 结尾的最长有效括号子串的长度。注意这个以 i 结尾是苛刻的它要求这个子串的最后一个字符就是s.charAt(i)所以如果s[i]是(那么dp[i]直接就是0因为任何有效括号串都不可能以左括号结尾。状态定义清楚了转移就只剩两种情况而且都要求s[i]是)。第一种情况s[i]是)且s[i-1]是(。这种情况最简单i-1和i直接凑成了一对括号那么以 i 结尾的最长有效串至少是2如果i-2位置之前还有有效串可以接上所以转移方程是dp[i] dp[i-2] 2当然要保证i-2不越界。第二种情况s[i]是)且s[i-1]也是)。这说明当前这个右括号要匹配的是它左边一段有效串之前的那个左括号。具体来说先看dp[i-1]它表示以i-1结尾的有效串长度假设为 len那么这段有效串覆盖的区间是[i-len, i-1]。在这段区间之前即下标i-len-1的位置如果是一个左括号那么它就能和当前的s[i]配对配对之后长度至少是dp[i-1] 2如果这个左括号前面还有有效串也要接上即再加上dp[i-len-2]。所以转移方程是dp[i] dp[i-1] 2 dp[i-dp[i-1]-2]需保证i-dp[i-1]-1 0且该位置是左括号这个方程初看很绕但拆开看非常清晰dp[i-1]是内部那段已经配好的有效串2 是外层新配的一对括号dp[i-dp[i-1]-2]是外层括号拼接位置之前的有效串。整个式子就像一个三明治前面已有的 新包的一层 中间已有的。3.2 动态规划 Java 代码与完整推导class Solution { public int longestValidParentheses(String s) { int n s.length(); int[] dp new int[n]; int max 0; for (int i 1; i n; i) { if (s.charAt(i) )) { if (s.charAt(i - 1) () { dp[i] (i 2 ? dp[i - 2] : 0) 2; } else if (i - dp[i - 1] - 1 0 s.charAt(i - dp[i - 1] - 1) () { dp[i] dp[i - 1] 2 (i - dp[i - 1] - 2 0 ? dp[i - dp[i - 1] - 2] : 0); } max Math.max(max, dp[i]); } } return max; } }用(()())完整推一遍长度6帮助理解i1s[1] 是)s[0] 是(第一种情况dp[1] dp[-1] 2 2。表示()。i2s[2] 是(跳过dp[2] 0。i3s[3] 是(跳过dp[3] 0。i4s[4] 是)s[3] 是(第一种情况dp[4] dp[2] 2 0 2 2。这里注意dp[2]0是因为索引2是孤立的左括号所以以4结尾的有效串是子串()从索引3到4。i5s[5] 是)s[4] 是)第二种情况。dp[4] 2检查i - dp[i-1] - 1 5 - 2 - 1 2s[2] 是(成立。于是dp[5] dp[4] 2 dp[5 - dp[4] - 2] 2 2 dp[1] 2 2 2 6。到 i5 时整个(()())被完整匹配答案6。仔细体会最后一步内部有效串是索引3-4的()长度2索引2的左括号和索引5的右括号配成外层一对索引0-1的()长度2作为前缀接上三个部分加起来就是6。边界条件上要特别小心i - dp[i-1] - 1可能等于 -1说明左边没有字符了这时不能访问数组i - dp[i-1] - 2同理。我用三目运算符做了保护这是写这类题目最常踩的坑稍微不留神就 ArrayIndexOutOfBoundsException。另一个细节是 dp 数组默认是0所以左括号位置的 dp 值天然是0不需要显式赋值。3.3 面试里讲 DP 思路的推荐顺序动态规划是面试官最喜欢的追问方向因为它能考察候选人有没有真正理解状态设计。我的建议是讲的时候按这个顺序来先说状态定义——dp[i] 表示以 i 结尾的最长有效括号子串长度然后说为什么左括号位置 dp 为0接着分两类讨论右括号的情况画一个字符串的括号配对图指着图说明第一类是最简单的相邻配对第二类是嵌套匹配需要借助 dp[i-1] 跳过内部区间最后强调边界保护和复杂度 O(n)。有一个常见的讲解误区是把第二种情况讲成看 s[i-1] 是不是左括号完全不对。s[i-1]是右括号时也可能匹配成功比如(())这种嵌套结构最后一个右括号匹配的是整个内部有效串之前的左括号。所以第二类才是这道题的精髓也是 DP 解法区别于其它解法的关键你能不能把这一条讲清楚面试官立刻就能判断出你是背的答案还是真懂。4. 双向计数法空间复杂度 O(1) 的最长括号解法4.1 单向计数为什么算不满答案如果能想到栈和 DP其实这道题已经能过了。但热题100里的好题通常会有追问能不能把空间复杂度降到 O(1)双向计数就是为这个问题准备的。思路是用两个计数器 left 和 right 扫描字符串。遇到(就 left遇到)就 right。当 left 等于 right 时说明当前这一段左右抵消是一个有效括号串长度就是2 * right更新最大值。当 right 大于 left 时说明从某个起点开始右括号已经超过了左括号这一段从该起点开始彻底没救了直接把 left 和 right 清零从下一个位置重新开始计数。这个单向扫描的思路很自然但它有一个致命缺陷它只能处理右括号太多导致的片段断裂处理不了左括号一直太多的情况。最典型的例子是(()从左往右扫i0 left1i1 left2i2 right1全程 left 始终大于 right永远不会触发重置也永远不会 left 等于 right所以答案一直是0。但实际上这个字符串里的()长度是2被漏掉了。问题出在哪因为(()里多了一个左括号而这个左括号在正向扫描中会被一直背在身上导致左右永远无法相等。要想解决它就得让多出来的左括号从另一个方向被消耗掉——这就有了反向扫描。4.2 双向计数 Java 代码与原理说明class Solution { public int longestValidParentheses(String s) { int left 0, right 0, max 0; int n s.length(); for (int i 0; i n; i) { if (s.charAt(i) () left; else right; if (left right) { max Math.max(max, 2 * right); } else if (right left) { left 0; right 0; } } left 0; right 0; for (int i n - 1; i 0; i--) { if (s.charAt(i) () left; else right; if (left right) { max Math.max(max, 2 * left); } else if (left right) { left 0; right 0; } } return max; } }反向扫描和正向扫描完全对称从右往左走遇到(让 left遇到)让 right当 left 等于 right 时更新2 * left。触发重置的条件变成left right因为从右往左看如果左括号数量超过了右括号说明这一段从右边起头没救了同样清零重新计。用(()再验证一次反向扫描从右往左i2 是)right1i1 是(left1left 等于 rightmax 2i0 是(left2此时 left 大于 right触发重置。最终答案2正确。同样正向能处理())这种右括号多的情况i0 left1i1 right1max2i2 right2right 大于 left触发重置答案2也正确。所以双向扫描合在一起能覆盖所有因为单边盈余导致的漏解。这个解法的复杂度和栈解法一样是 O(n) 时间但空间复杂度只有 O(1)只用了两个计数器。它的正确性依赖于一个事实任何有效括号串从左往右看任意前缀右括号不超过左括号从右往左看任意后缀左括号不超过右括号。双向扫描把两个方向的约束都验证一遍就能保证不被单向盈余骗过去。4.3 O(1) 空间方案在面试中的定位这道题在面试中经常作为栈题目的空间优化追问出现。面试官看你写完栈解法后很可能会来一句能不能不用额外空间这时候如果你能直接写出双向计数并且解释清楚为什么单向不行、两个方向各自能捕获哪种盈余括号基本就稳了。但我要提醒一句不要因为这个解法空间最优就在面试一开始就抛它。双向计数的推导过程不如栈直观如果面试官期待的是一步步引导你想到栈你上来就讲计数法反而容易显得流程跳跃。稳妥的策略是先给出最自然的栈解法并解释清楚主动提一句这个思路空间是 O(n)如果面试官希望优化我还有 O(1) 的双向计数方案。这种节奏既展示深度又展示沟通能力是面试里很加分的处理方式。5. 三种解法复杂度对比与面试答题策略5.1 复杂度与代码量对照表三种解法的复杂度对比如下我整理成了一张速查表方便你复习时一眼扫过解法时间复杂度空间复杂度核心思想代码量面试推荐度栈O(n)O(n)最近匹配 哨兵边界约15行必写动态规划O(n)O(n)以 i 结尾的状态转移约20行加分项双向计数O(n)O(1)贪心计数 反向修正约25行优化项时间复杂度都是 O(n)因为每个字符都只被常数次操作处理。空间上栈和 DP 都是 O(n)只有双向计数是 O(1)。有意思的是代码量最少的栈解法反而不是空间最优而空间最优的双向计数代码量反而偏大因为要写两遍几乎一样的循环。这告诉我们一个道理时间复杂度的极限是 O(n)但空间复杂度的极限可以压缩到 O(1)面试时你要根据追问方向决定展示哪个。还有一个容易被忽略的点DP 解法虽然代码看起来规整但它的常数项其实比栈要大因为每个字符判断的 if 分支更多而且 dp 数组的随机访问在内存不友好时会慢一些。不过对于 n3×10⁴ 这个量级三种解法耗时都在毫秒级肉眼根本看不出差别。刷题阶段不要纠结微秒级的性能差异重点是把每种解法的思想吃透。5.2 不同基础选手的答题顺序建议如果你是刚开始刷力扣热题100的新手我的建议是只盯栈解法。原因有三个第一栈解法思路直白和括号匹配的基础题衔接紧密几乎不需要额外推导第二代码只有15行左右出 bug 的概率最小手写代码时最稳第三它能直接在 O(n) 时间内解决问题面试已经合格。把栈解法练到能闭着眼睛写出来包括哨兵 -1 的细节这一题就算过关了。如果你已经有了一定刷题量目标是系统性提升那就把三种解法都吃透并且重点放在 DP 和双向计数上。DP 能让你训练以 i 结尾这类子串问题的通用建模能力双向计数能让你体会贪心 方向修正的思维范式。这两种思维在其它题目里会反复出现比如接雨水、最长回文子串、盛最多水的容器都有它们的影子。如果是面试冲刺阶段我建议你在纸上把三种解法的思路纲要各写一遍练习用30秒讲清每一种的核心理由。面试官问还有别的方法吗时你可以先说 DP再说 O(1) 空间展示完整的思考链条。这个过程本身就是算法思维升华的过程很多人刷了几百题但面试表现一般差别不在于代码能力而在于能不能把思路组织成有层次的表达。6. 刷题实战高频报错与调试排查经验6.1 高频错误速查表这道题我刷了三遍也在面试中面过别人发现错误出现的位置高度集中。我把常见问题整理成了一张速查表错误现象根本原因修复方式答案偏大把无效子串也算进去了子串和子序列混淆没有保证连续检查每个解法是否都在处理连续片段栈解法返回0忘在初始化时 push(-1)循环前先压哨兵栈解法答案偏小右括号弹栈后栈空时没有 push(i) 作为新边界补上else { stack.push(i); }DP 报数组越界i - dp[i-1] - 1或i - dp[i-1] - 2为负用三目运算符或 if 判断保护双向计数答案偏小反向扫描的重置条件写错反向必须用left right触发重置输入空串或单个括号返回了意外值没有考虑 n0 或 n1 的边界循环天然不执行dp[0] 默认0验证一下即可你如果有哪一项中了先别急着抄答案回到代码里定位是哪个分支漏了。这道题错误高度集中说明它的正确性对细节极度敏感而这正是面试官喜欢拿来考人的原因。调试的时候我强烈建议用 IDE 的 Debugger 而不是 System.out 打点。在栈解法里逐步查看每次 push、pop 后栈的内容在 DP 解法里观察 dp 数组从0到 n 的变化过程。特别是 DP 的第二种转移你肉眼很难直接看出i - dp[i-1] - 1到底指向哪IDE 里把这一步的每个变量都展开看一遍瞬间就通了。6.2 测试用例设计与调试技巧面试手写代码时最忌讳写完之后直接交卷说写完了。我见过很多候选人逻辑没问题但边界用例一测就挂。这道题我建议你至少在纸上测这几组用例空串答案0(、)单个括号答案0()简单配对答案2(()左盈余答案2())右盈余答案2()()连续平级答案4(())嵌套答案4(()())嵌套加平级的混合答案6)()())力扣官方示例答案4((()))三层嵌套答案6我自己的经验是把(()和())这一对用例放在最前面测因为它们是单方向盈余的代表一次能同时验证正向和反向逻辑。刷题时如果这组用例过了再补一个混合的)()())基本就能覆盖九成错误。如果用了 DP 解法再多测一个(()())专门验证第二种转移和边界保护。还有一个工程上的小细节力扣的环境里字符串用的是 Unicode题目保证只有半角英文括号但如果你从本地文件或终端粘贴测试用例很可能混入全角括号或者不可见空格这时 charAt 拿到的字符不等于(代码会安静地跳过输出结果就错了。遇到答案莫名小的时候先检查输入字符串是不是干净。6.3 变体题与扩展思考这道题的变体非常多在面试中经常被改装。最常见的变体是要求输出最长有效括号子串本身而不仅仅是长度。解法需要额外记录最大长度对应的起始位置当max被更新时同步把起点设为i - max 1以栈解法为例最后用substring截取即可。这个改动很小但能帮你把长度计算和区间定位建立联系我建议你花10分钟改一版。第二个变体是把括号类型扩成三种像力扣20题有效括号那样涉及()[]{}的匹配。这时 DP 和双向计数就不好使了因为配对关系从一种符号变成了三种符号只能靠栈而且判断条件里要检查栈顶是否是对应的左括号。从这个变体能看出栈解法的可扩展性是最强的。第三个变体是最长有效括号子序列即允许跳过字符。这个反而简单很多答案就是2 * min(left总数量, right总数量)只需要统计总括号数不需要任何复杂算法。很多面试官故意先问子序列版本让你放松警惕再把条件收紧成子串考察你能否意识到连续带来的难度差异。你如果每次都能主动指出这两个问题的本质区别会给面试官留下很好的印象。7. 算法思维沉淀从一道括号题看套路体系7.1 栈类题型的识别信号刷题量上来之后你就会发现栈不是为这道题量身定做的而是一类问题的通用工具。识别信号有三个需要处理最近配对、需要处理抵消关系、需要处理回退到最近状态。括号匹配、表达式求值、函数调用栈、浏览器后退按钮全是这个套路。这道题教给我们的栈技巧有两个值得沉淀一是用哨兵元素-1避免空栈时的特判这个技巧在柱状图中最大的矩形里也用得到二是栈里存下标而不是值让栈从一个数据结构变成位置索引的追踪器遇到需要计算区间长度的问题优先考虑存储下标。有了这两个意识栈解法就不再是背代码而是遇到问题时的自然条件反射。7.2 以 i 结尾 DP 套路的延伸dp[i]表示以 i 结尾的某种状态这是子串类动态规划最强的套路之一。最长有效括号、最长回文子串、最长递增子序列严格说那是以 i 结束的最长递增子序列、最大子数组和全都是同一个模板定义以 i 结尾的状态然后根据当前位置和前一个位置的关系做转移。这个套路的核心理解方式是当你处理到第 i 个位置时不要去想从某个起点开始的整个区间而是只去想以 i 结尾这一段怎么接上之前的结果。就像搭积木每次只看最后一块积木怎么放上去而不是重新搭一整面墙。如果你能从这个角度理解 DP看到最长 xx 子串这类题的第一反应就不会是枚举所有子串而是想状态定义。7.3 复盘方法一道题沉淀三类解法最后聊聊复盘。很多人刷题是AC了就算过今天写完明天忘我觉得是因为少了解法对比这一步。我的习惯是刷完一道题之后强制自己回答三个问题这道题的最优时间复杂度和空间复杂度是多少除了标准解法还有没有其它角度哪种解法最适合在面试中引导式讲出来对最长有效括号这道题三个问题的答案分别是最优时间 O(n)最优空间 O(1)除了栈还有 DP 和双向计数面试讲解首选栈优化追问再上 DP 或者双向计数。每次复盘都这样过一遍你的算法思维才会形成体系而不是散成一堆孤立题解。一道好题的价值不在于AC时的爽快而在于你从它身上提炼出的那几个可迁移的思维锚点。回到题目本身(())、()()、(()())这些用例的答案我都亲手推过3×10⁴ 的长度限制也实测过三种解法在毫秒级完成。做题时最让我意外的不是解法有多精妙而是最简单的计数思想加上反向扫描居然能达到和 DP 一样的效果有的题就是这样绕了一大圈真正的钥匙往往藏在最朴素的角度里但只有你把所有解法都走过一遍之后才看得见它。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。