leetcode-stack- 最长有效括号

501 阅读1分钟

题目

给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。 示例 1:

输入:s = "(()" 输出:2 解释:最长有效括号子串是 "()" 示例 2:

输入:s = ")()())" 输出:4 解释:最长有效括号子串是 "()()" 示例 3:

输入:s = "" 输出:0  

提示:

0 <= s.length <= 3 * 104 s[i] 为 '(' 或 ')'

利用stack

public static int longestValidParentheses(String s) {

    int max = 0;
    Stack<Integer> stack = new Stack<>();
    //需要注意的是,如果一开始栈为空,第一个字符为左括号的时候我们会将其放入栈中,
    //这样就不满足「最后一个没有被匹配的右括号的下标」,
    //为了保持统一,我们在一开始的时候往栈中放入一个值为 -1−1 的元素
    stack.push(-1);
    for (int index = 0; index < s.length(); index++) {
        char c = s.charAt(index);
        if (c == '(') {
            stack.push(index);
        } else {
            //对于遇到的每个 ‘)’,我们先弹出栈顶元素表示匹配了当前右括号 ())
            //如果栈为空,没有匹配到右括号。我们将其下标放入栈中,标示最后一个没有被匹配的右括号的下标
            //如果栈不为空,匹配到了右括号。当前右括号的下标减去栈顶元素即为最长右括号长度
            stack.pop();
            if (stack.isEmpty()) {
                stack.push(index);
            } else {
                max = Math.max(max, index - stack.peek());
            }
        }
    }
    return max;
}

二次遍历,不需要额外的空间

在此方法中,我们利用两个计数器 \textit{left}left 和 \textit{right}right 。首先,我们从左到右遍历字符串,对于遇到的每个 \text{‘(’}‘(’,我们增加 \textit{left}left 计数器,对于遇到的每个 \text{‘)’}‘)’ ,我们增加 \textit{right}right 计数器。每当 \textit{left}left 计数器与 \textit{right}right 计数器相等时,我们计算当前有效字符串的长度,并且记录目前为止找到的最长子字符串。当 \textit{right}right 计数器比 \textit{left}left 计数器大时,我们将 \textit{left}left 和 \textit{right}right 计数器同时变回 00。

这样的做法贪心地考虑了以当前字符下标结尾的有效括号长度,每次当右括号数量多于左括号数量的时候之前的字符我们都扔掉不再考虑,重新从下一个字符开始计算,但这样会漏掉一种情况,就是遍历的时候左括号的数量始终大于右括号的数量,即 (() ,这种时候最长有效括号是求不出来的。

解决的方法也很简单,我们只需要从右往左遍历用类似的方法计算即可,只是这个时候判断条件反了过来:

当 \textit{left}left 计数器比 \textit{right}right 计数器大时,我们将 \textit{left}left 和 \textit{right}right 计数器同时变回 00 当 \textit{left}left 计数器与 \textit{right}right 计数器相等时,我们计算当前有效字符串的长度,并且记录目前为止找到的最长子字符串

/**
 * 不需要额外空间法
 *
 * @param s
 * @return
 */
public static int longestValidParentheses2(String s) {
    int left = 0;
    int right = 0;
    int max = 0;
    for (int i = 0; i < s.length(); i++) {
        if (s.charAt(i) == '(') {
            left++;
        } else {
            right++;
        }
        if (right == left) {
            max = Math.max(max, 2 * left);
        } else if (right > left) {
            left = right = 0;
        }
    }
    left = right = 0;

    //反向遍历
    for (int j=s.length()-1;j>=0;j--){
        if (s.charAt(j) == '(') {
            left++;
        } else {
            right++;
        }
        if (right == left) {
            max = Math.max(max, 2 * left);
        } else if (left > right) {
            left = right = 0;
        }
    }
    return max;
}

测试用例

public static void main(String[] args) {
    int i = longestValidParentheses2("()(())");
    System.out.println("maxLength=" + i);
}