LeetCode 32. 最长有效括号
题目描述

题意分析
给定一个只包含
'('和')'的字符串,找出其中最长的连续有效括号子串,返回它的长度。有效子串要求左右括号能够完整匹配,且任意前缀的右括号数不超过左括号数。子串必须连续,不能跳过中间的字符,也不能把分散的匹配对数相加。需要识别哪些位置会截断一段合法区间,以及当前匹配完成后,这段连续区间最早能从哪里开始。空字符串或没有有效子串时,答案为
0。
解法:栈维护未匹配下标
核心思路
[!blue]
有效括号子串一定以右括号结尾。扫描每个右括号时,只要能求出以当前位置结尾的最长有效长度,取所有位置中的最大值就是答案。栈除了用来匹配括号,还要保存下标,才能计算连续区间的长度。
栈底保存当前可匹配区域之前的边界:开始时放入
-1,表示字符串起点之前;之后如果遇到无法匹配的右括号,就把它的下标设为新边界。合法子串不能跨过这个失配位置。栈底之上保存尚未匹配的左括号下标,越靠栈顶,出现得越晚。遇到左括号,直接将下标入栈。遇到右括号,先弹出栈顶:若原来有未匹配的左括号,这次弹出完成一对匹配,栈内至少还剩边界;若弹出后栈空,说明刚才弹掉的是边界,没有左括号可以配对,当前右括号必须成为新的边界,此时不能计算有效长度。
成功匹配后,设剩余栈顶下标为
b。它要么是最近一个仍未匹配的左括号,要么是栈底边界;它后面的字符到当前位置i已全部匹配,所以区间[b + 1, i]有效。这个区间不能再向左扩展,否则会包含一个尚未匹配的左括号或失配右括号。因此它正是以i结尾的最长有效子串,长度为i - b。已经匹配的下标会被弹出,因而后续相邻或嵌套的有效部分能够自然合并成更长区间。哨兵
-1让从下标0开始的有效子串也能使用同一长度公式,无需额外分支。
解题步骤
- 初始化答案为
0,将哨兵-1入栈。- 从左向右遍历下标
i;遇到'('时将i入栈,继续处理下一个字符。- 遇到
')'时弹栈。若弹出后栈空,将i入栈,作为后续有效区间不能跨越的新边界。- 若弹出后栈非空,用
i - stack.peek()更新最大长度。遍历结束后返回答案。
代码实现
class Solution {
public int longestValidParentheses(String s) {
Deque<Integer> stack = new ArrayDeque<>();
// 起点之前的边界,使从下标零开始的有效段也能直接相减。
stack.push(-1);
int result = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
stack.push(i);
continue;
}
stack.pop();
// 当前右括号没有可配左括号,改记为新的失配边界。
if (stack.isEmpty()) {
stack.push(i);
} else {
// 剩余栈顶位于当前最长有效后缀之前,长度无需加一。
result = Math.max(result, i - stack.peek());
}
}
return result;
}
}
func longestValidParentheses(s string) int {
// 起点之前的边界,使从下标零开始的有效段也能直接相减。
stack := []int{
-1,
}
result := 0
for i := range s {
if s[i] == '(' {
stack = append(stack, i)
continue
}
stack = stack[:len(stack)-1]
// 当前右括号没有可配左括号,改记为新的失配边界。
if len(stack) == 0 {
stack = append(stack, i)
} else if length := i - stack[len(stack)-1]; length > result {
// 剩余栈顶位于当前最长有效后缀之前,长度无需加一。
result = length
}
}
return result
}
复杂度分析
- 时间复杂度:$O(n)$,其中
n为字符串长度。只遍历一次字符串,每个下标最多入栈、出栈各一次。- 空间复杂度:$O(n)$,最坏情况下字符串全为左括号,栈需要保存所有下标及哨兵。
关键点总结
[!green]
- 栈底记录不可跨越的边界,其余位置记录尚未匹配的左括号;两类下标的作用不同。
- 弹栈匹配之后再读栈顶,才能得到当前最长有效后缀之前的位置。
- 每个右括号位置都计算一次可形成的最长有效后缀,覆盖了所有有效子串可能的结束位置。
易错点总结
[!yellow]
- 弹栈后必须先判断是否为空,再读取栈顶;空栈时压入当前下标,才能建立新的边界并保证下一轮有可弹出的元素。
- 长度使用弹栈后的栈顶计算,不能使用刚弹出的左括号下标,否则只能看到最近这一对,无法连接前面已匹配的部分。
b是有效区间前一个位置,区间实际从b + 1开始,所以长度是i - b,不再加1;初始边界也必须为-1。- 只统计匹配对数无法保证连续,必须通过未匹配下标确定有效区间的边界。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 20. 有效的括号 | 简单 | 匹配括号的基础语义相同,原题只判整串,本题还需记录下标或长度寻找最长合法段。 |
| 301. 删除无效的括号 | 困难 | 本题保留连续区间,原题可删除任意位置获得最长合法子序列式结果,目标不同。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!