32. 最长有效括号 | 刷题打卡

234 阅读2分钟

前言

今天看面试题,就看到了这道面试。只不过面试官是从最简单的判断一个字符串,是不是有效括号。
然后再提出"求有效括号的最长长度",最后我就想去直接入手这道题。

题目描述

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

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

示例 2:

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

示例 3:

输入:s = ""
输出:0

解题思路

解题思路一:常规

对于这种括号匹配问题,一般都是使用栈。 我们先找到所有可以匹配的索引号,然后找出最长连续数列!

具体步骤:

  1. 当碰到了"(",就将"("压入stack栈中,tmPmax++。

  2. 当碰到了")",就将前一个")"出栈,tmPmax++。

  3. 计算max(最长有效长度)

  • 在"("进栈时,判断stack.length 是否等于 0, 等于0就要进行计算了(max = max>tmpMax?max:tmpMax )。由于出现")()()(()",在碰到"("(index==4),就进行新的有效括号判断。

  • 在碰到")"时,判断stack.length 是否等于 0,等于0就要进行计算了(max = max>tmpMax?max:tmpMax )。由于碰到")",后面一定不是有效括号

  • 在一次循环后stack.length == 0,进行计算。

AC代码

var longestValidParentheses = function(s) {
    var max = 0;  // 最长长度
    if(s.length == 0 || s.length == 1) return max;
    var stack = [].fill(')');  // 使用了栈来实现匹配 空间复杂度O(n)
    for(var i =0;i<s.length; i++) {
        var tmpMax = 0;
        var temp = 0;
        for(var j = i;j<s.length;j++){
            if(s[j] == '('){
                if(stack.length == 0) {
                    max = max>tmpMax?max:tmpMax 
                    temp = 0
                }
                stack.push('(')
                tmpMax++;
                temp++;
            }else if(s[j] == ')') {
                if(stack.length < 1){
                    // 栈空的 当前位置括号有效匹配 结束了
                    max = max>tmpMax?max:tmpMax 
                    break;
                }else{
                    stack.pop();
                    tmpMax++;
                    temp++
                }
            } 
        }
        if(stack.length == 0) {
            max = max>tmpMax?max:tmpMax
        }
        stack = []; 
    }
    return max;
};

解题思路二:减少时间复杂度

  1. 对于遇到的每个 "(" ,我们将它的下标放入栈
  2. 对于遇到的每个 ")" ,我们先弹出栈顶元素表示匹配了当前右括号:
    • 如果栈为空,说明当前的右括号为没有被匹配的右括号, 我们将其下标放入栈中来更新我们之前提到的「最后一个没有被匹配的右括号的下标」
    • 如果栈不为空,当前右括号的下标减去栈顶元素 即为「以该右括号为结尾的最长有效括号的长度」

AC代码

function longestValidParentheses(s) {
    // 时间复杂为O(n)
    var max = 0;
    if(s.length == 0 || s.length == 1) return max;
    var stack = [-1]; // 栈来服务于有效括号匹配 刚开始匹配的
    for(var i = 0; i< s.length;i++){  // 下标法
        if(s[i] == '('){// 左括号
            stack.push(i);
        }else {
            stack.pop(); // 右括号 出栈
            if(stack.length < 1) {
                stack.push(i) // 
            } else {
                max = Math.max(max, i - stack[stack.length -1])
            }
        }
    }
    return max;

}

解题思路二:减少空间复杂度

我们利用两个计数器 left 和 right 。

  1. 首先,我们从左到右遍历字符串,对于遇到的每个 "(",我们增加 left计数器, 对于遇到的每个 ")" ,我们增加 right 计数器。

  2. 每当 left 计数器与 right 计数器相等时,我们计算当前有效字符串的长度,并且记录目前为止找到的最长子字符串。

  3. 当 right 计数器比 left 计数器大时,我们将 left 和 right 计数器同时变回 0。

  4. 该贪心的缺点:

    • 这样会漏掉一种情况,就是遍历的时候左括号的数量始终大于右括号的数量,即 (() ,这种时候最长有效括号是求不出来的。
  5. 解决: 只需要从右往左遍历用类似的方法计算即可(判断条件有所改变)

AC代码

let left = 0, right = 0, maxlength = 0;
    for (let i = 0; i < s.length; i++) {
        if (s[i] == '(') {
            left++;
        } else {
            right++;
        }
        if (left == right) {
            maxlength = Math.max(maxlength, 2 * right);
        } else if (right > left) {
            left = right = 0;
        }
    }
    left = right = 0;
    for (let i = s.length - 1; i >= 0; i--) {
        if (s.charAt(i) == '(') {
            left++;
        } else {
            right++;
        }
        if (left == right) {
            maxlength = Math.max(maxlength, 2 * left);
        } else if (left > right) {
            left = right = 0;
        }
    }
    return maxlength;

总结

一个代码的空间复杂度和时间复杂度,是面试官算法考试的重点。只能说优秀的代码还在后面呢!

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情