32 最长有效括号

19 篇文章 0 订阅
5 篇文章 0 订阅
本文详细介绍了四种解决括号匹配问题的算法:栈、动态规划、左右扫描法。每种方法的时间复杂度和空间复杂度都为O(n),通过实例分析了它们的工作原理和适用场景。对于括号序列的有效匹配,提供了清晰的思路和代码实现。
摘要由CSDN通过智能技术生成

题目

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

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

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

示例 3:
输入:s = “”
输出:0

  • 方法一:栈,按右括号分割版
class Solution {
public:
    int longestValidParentheses(string s) {
        int maxlen = 0;
        int start = 0;//当前有效括号序列的起始位置
        stack<int> index;//储存左括号的下标
        for(int i=0; i<s.size(); i++){
        	//如果此时没有左括号而直接出现了右括号,匹配失败
            if(index.empty()&& s[i] ==')'){
                start = i+1;
                continue;
            }
            //左括号则入栈,右括号且栈非空则出栈并计算序列长度
            if(s[i] =='('){
                index.push(i);
            }else{
                index.pop();
                //如果栈空则类似()(())
                //栈非空则类似(()()
                maxlen = index.empty() ? max(maxlen,i-start+1) : max(maxlen, i-index.top());
            }
        }
        return maxlen;
    }
};
  • 时间复杂度O(n)

  • 空间复杂度O(n)

  • 思路

    • 计算每一个以匹配成功的右括号为右端点的有效括号长,将它与当前的最大值比较,大于则更新。遍历字符串,以右括号为分类标准依次处理,遇到左括号则将它的下标入栈。
      • 如果为右括号且此前没有左括号(栈空),则此前均无有效括号,有效括号匹配的起始位置start为此时下标加一,跳过该重循环进入下一层循环。
      • 栈非空,此时遇到右括号则匹配成功,将此时的左括号出栈。如果此时栈空,则表示当前的最长有效括号已经处理完了,计算此时的长度为当前右括号的下标减去start加1.如果此时栈非空,则表示匹配成功的左括号为当前栈顶元素的下一个元素,计算此时的长度为当前右括号的下标减去栈顶元素。
  • 方法二:栈,计算区间长度版

class Solution {
public:
    int longestValidParentheses(string s) {
        int maxlen = 0;
        stack<int> stk;
        stk.push(-1);//起始标志位
        for(int i=0; i<s.size(); i++){
        	//左括号则入栈
            if(s[i] == '('){
                stk.push(i);
            }else{
            	//右括号则出栈且计算长度
                stk.pop();
                if(stk.empty()){
                    stk.push(i);
                }else{
                    maxlen = max(maxlen, i-stk.top());
                }
            }
        }
        return maxlen;
    }
};
  • 时间复杂度O(n)

  • 空间复杂度O(n)

  • 思路

    • 根据匹配失败的括号来分割区间,计算每一次匹配成功时的有效括号长度
    • 先将-1入栈,作为标志位。
    • 如果为左括号则入栈,为右括号则出栈。如果栈非空,则表示匹配成功,此时的栈顶元素为成功匹配的左括号的前一个字符,计算此时的有效长度。如果栈空则表示匹配失败,将当前元素的下标作为标志位,入栈,表示下一次匹配的起始区间。
  • 方法三:动态规划

class Solution {
public:
    int longestValidParentheses(string s) {
        int maxlen = 0;
        vector<int> dp(s.size());
        //dp[i]表示以i结尾的最长有效括号长度
        //动态规划
        for(int i=1; i<s.size(); i++){
        	//如果是左括号,有效括号长度为0
            if(s[i] == '(') dp[i] = 0;
            else{//如果为右括号,有两种情况
            	//它的前一个为左括号
                if(s[i-1] == '(') dp[i] = i-2 >=0 ? dp[i-2] +2 : 2;
                else{//它的前一个为右括号
                    if(i-dp[i-1]-1>=0 && s[i-dp[i-1]-1] == '(')
                    dp[i] = (i-dp[i-1]-1 == 0) ? 2+dp[i-1] :dp[i-dp[i-1]-2] +2 +dp[i-1];
                }
            }
            maxlen = max(maxlen, dp[i]);
        }
        return maxlen;
    }
};
  • 时间复杂度O(n)

  • 空间复杂度O(n)

  • 思路

    • dp[i]表示以s[i]结尾的最长有效括号长度
    • 确定递推公式
      • 如果s[i]==’(’,必不匹配,dp[i]=0
      • 如果s[i]==’)’,分两种情况
        • 如果s[i-1]==’(’,此时已经有一对匹配的括号了,dp[i] = dp[i-2]+2。表示新匹配的括号长度加上其子问题中匹配的括号长度。
        • 如果s[i-1]==’)’,寻找s[i]所对应的左括号s[i-dp[i-1]-1],如果为左括号,则表示匹配成功dp[i] = dp[i-1]+2+dp[i-sdp[i-1]-2]。表示已匹配的括号长度2,加上前一个右括号匹配的括号长度dp[i-1],加上其子问题即这两个括号之前匹配的括号长度即dp[i-dp[i-1]-2]。
  • 方法四:左右扫描法

class Solution {
public:
    int longestValidParentheses(string s) {
        int maxlen = 0;
        int left=0, right = 0;
        //两次扫描
        //自左向右
        for(int i=0; i<s.size(); i++){
            if(s[i] == '(') left++;
            else right++;
            if(left == right){
                maxlen = max(maxlen, 2*left);
            }
            if(left<right){
                left = 0;
                right = 0;
            }
        }
        left = 0;
        right = 0;
        //自右向左
        for(int i=s.size()-1; i>=0; i--){
            if(s[i]=='(') left++;
            else right++;
            if(left == right){
                maxlen = max(maxlen, 2*left);
            }
            if(left>right){
                left = 0;
                right = 0;
            }
        }
        return maxlen;
    }
};
  • 时间复杂度O(n)
  • 空间复杂度O(1)
  • 思路
    • 贪心。自左向右开始扫描,当括号匹配时左括号一定等于右括号,只有一种情况除外,即左括号一直大于右括号数,故扫描两遍。
    • 自左向右扫描,每当左括号数目等于右括号数目时,计算匹配长度。如果右括号数量超过左括号数量,表示此前括号已经匹配结束,将括号数量重新设置为0。该方法在左括号始终比右括号多时无法计算有效括号序列,故扫描两边。
    • 自右向左扫描,每当左括号数目等于右括号数目时,计算匹配长度。如果左括号数量超过右括号数量,表示此前括号已经计算完毕,将括号数量清零。

栈右括号分割版参考详解
动态规划参考详解
左右扫描法参考详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值