一、题目要求
给你一个只包含 '(' 和 ')' 的字符串 s,
请你找出 最长连续的、格式正确的括号子串的长度。
示例:
输入:")()())"
输出:4
解释:最长有效括号子串是 "()()"
注意关键词:
- 连续子串(不能跳)
- 有效括号(左右必须正确匹配)
二、常见误区
很多人第一反应是:
- 数一共有多少对括号
- 或者用计数器左右抵消
但这些方法都会忽略一个核心问题:
有效括号必须是“连续区间”
一旦中间断了,前面的就不能算。
三、核心思路:栈 + 下标
这道题的关键不是“匹配括号”,而是:
找到每一段合法括号区间的长度
关键设计思想
-
栈中 不存括号字符
-
栈中只存 下标
-
栈顶始终代表:
- 当前这段合法括号子串左边最近的断点
为什么一开始要压入 -1
这是整道题的灵魂点。
stack.push(-1);
含义是:
- 在字符串开始前,放一个“虚拟断点”
- 这样当合法括号从索引 0 开始时,也能正确算长度
否则 "()" 这种最简单情况都算不出来。
四、完整代码
class Solution {
public int longestValidParentheses(String s) {
int maxLen = 0;
Deque<Integer> stack = new ArrayDeque<>();
// 关键:先放一个 -1,作为“基准下标”
stack.push(-1);
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 {
// 可以匹配,计算当前合法区间长度
maxLen = Math.max(maxLen, i - stack.peek());
}
}
}
return maxLen;
}
}
五、逐行分析代码在“干什么”
1. 栈的本质作用
Deque<Integer> stack = new ArrayDeque<>();
栈里存的是:
“不能参与当前合法区间的下标”
不是括号本身。
2. 遇到左括号 (
stack.push(i);
含义:
- 这个左括号可能成为一个匹配的起点
- 先记录下标,等右括号来配
3. 遇到右括号 )
stack.pop();
先尝试“配对”:
- 弹出一个左括号(或者断点)
情况一:栈空了
if (stack.isEmpty()) {
stack.push(i);
}
说明:
- 当前这个
)没有任何左括号可以匹配 - 它本身就是一个新的“非法断点”
- 后面的合法子串,只能从它之后开始算
情况二:栈没空
maxLen = Math.max(maxLen, i - stack.peek());
此时:
-
stack.peek()指向的是- 当前合法括号子串左边最近的断点
-
i - stack.peek()就是- 当前这段合法括号的长度
六、用一句话理解整个算法
栈里存的不是括号,而是
“当前合法括号子串左边,最后一个不合法位置的下标”
每当成功匹配一次,就用当前位置减去这个断点,算出长度。
七、复杂度分析
- 时间复杂度:
O(n)
每个字符最多进栈、出栈一次 - 空间复杂度:
O(n)
最坏情况下栈存所有左括号