题目
给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。
示例 1:
输入: "(()"
输出: 2
解释: 最长有效括号子串为 "()"
示例 2:
输入: ")()())"
输出: 4
解释: 最长有效括号子串为 "()()"
解题思路
我们可以通过栈,在遍历给定字符串的过程中,判断到目前位置的子串的有效性。然后更新最长的有效长度。
具体做法
- 对于遇到的每一个
(
,我们将他的下标放入栈中。 - 当扫描到
)
,则弹出一个次,并记录有效长度为:当前索引 - 出栈索引 + 1,更新最大值。
但是示例2中我们可见,一开始我们扫描到的就是)
,此时栈为空,无法弹出。并且如果在扫描的过程中,栈为空,我们再次遇到)
,也不能继续向下进行。因此我们需要设立一个参照物,确保栈在整个扫描的过程中不为空,可计算。
- 对于开始扫描时,设置栈中初始元素为 -1,如果顺利扫描如
(())
这样的字符串,则有效长度为3-(-1) = 4
没有任何影响,此时-1为栈顶值。 - 对于开始扫描时,设置栈中初始元素为 -1,如果描如
)())
这样的字符串,遇到)
直接-1出栈,此时栈为空,再次入栈的如果为(
的索引即1,则再次遇到)
出栈时,计算会出错,长度为2-1 = 1
。所以我们同样需要一个参照元素用以计算有效长度。可以选择栈为空时候遇到的右括号的索引作为参照元素,恰巧比下一个入栈(
的索引小1。便于统一计算。 - 遇到连续的
)
,也会不断弹出上一个参照元素。弹出机制可以保持栈中的参照元素时最接近下一个(
的元素。
/**
* @param {string} s
* @return {number}
*/
var longestValidParentheses = function(s) {
var maxLen = 0;
const stack = [-1];
for (let i = 0; i < s.length; i++) {
const c = s[i];
if (c == '(') { // 左括号的索引入栈
stack.push(i);
continue; // 跳过,考察下一个符号
}
stack.pop(); // 遇到右括号,栈顶出栈
if (stack.length == 0) { // 栈变空了,右括号匹配不到人了
stack.push(i); // 说明它要充当“参照物”了
} else { // 右括号找到匹配,计算有效的连续长度,挑战最大
maxLen = Math.max(maxLen, i - stack[stack.length - 1]);
}
}
return maxLen;
};