动态规划+双向遍历记录括号+堆栈记录下标得到匹配括号长度 32. 最长有效括号

这是一篇关于解决最长有效括号问题的博客,通过动态规划和堆栈两种方法来找到字符串中有效括号的最长子串。文章详细介绍了如何使用动态规划的状态转移方程,以及利用堆栈记录下标来判断匹配括号的长度。通过从左到右和从右到左遍历字符串,找到不同类型的括号匹配情况,从而求得最长有效括号的长度。
摘要由CSDN通过智能技术生成

32. 最长有效括号

给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。

示例 1:

输入: "(()"
输出: 2

解释: 最长有效括号子串为 “()”
示例 2:

输入: ")()())"
输出: 4

解释: 最长有效括号子串为 “()()”

解题
将括号可能出现的顺序分类:
(1)‘()()()()’ 任意方向遍历可以得到
(2)‘((()))’ 任意方向遍历可以得到
(3)‘(((()’ 从右到左遍历可以得到
(4)‘()))))’ 从左到右遍历可以得到

暴力法:取出每一个子串进行计算(超时)
对s中每一个长度不小于2的子串进行从左到右遍历(用堆栈记录也行,用leftright’记录也行),找到最大长度的子串;

//堆栈记录
class Solution {
public:
    int longestValidParentheses(string s) {
        int n=s.size();
        int MAXLEN=0;
        for(int i=0;i<n-1;i++)
            for(int j=2;j<=n-i;j++)
        {
            MAXLEN=max(MAXLEN,valid(s.substr(i,j)));
        }
        return MAXLEN;
    }

    int valid(string s) {
        stack<char> match;
        int n=s.size();
        int len=0;
        int MAXLEN=0;
        for(int i=0;i<n;i++)
        {
            if(s[i]=='(') match.push('(');
            else {
                if(match.empty()) 
                    len=0;
                else {
                    len+=2;
                    match.pop();
                }
            }
            if(match.empty())
                if(len>MAXLEN)
                    MAXLEN=len;
        }
        return MAXLEN;
    }
};
//leftright记录
class Solution {
public:
    int longestValidParentheses(string s) {
        int n=s.size();
        int MAXLEN=0;
        for(int i=0;i<n-1;i++)
            for(int j=2;j<=n-i;j++)
        {
            MAXLEN=max(MAXLEN,valid(s.substr(i,j)));
        }
        return MAXLEN;
    }

    int valid(string s) {
        stack<char> match;
        int n=s.size();
        int MAXLEN=0;
        int left=0;
        int right=0;
        for(int i=0;i<n;i++)
        {
            if(s[i]=='(') left++;
            else right++;
            if(left==right) MAXLEN=max(MAXLEN,right+left);
            else if(left<right) left=right=0;
        }
        return MAXLEN;
    }
};

动态规划:分类过多,难以想到
dp[i]表示以下标为i结尾的子串的有效括号长度;
(1)若s[i]== ‘(’,则不会有以 ‘(’结尾的有效括号,都为0;
(2)若s [i] == ‘)’s[i-1]=='(' ,dp[i]=2——当i>2时要加dp[i-2],因为前面可能也有有效括号;
(3)若 s [i] == ‘)’s[i-1] == ‘)’s[i-dp[i-1]-1]=='(',说明该右括号有左括号匹配,dp[i]=dp[i-1]+2+dp[i-dp[i-1]-2];,还要加上匹配的做括号之前的dp数量;
过程中记录最大的dp;

class Solution {
public:
    int longestValidParentheses(string s) {
        int n=s.size();
        if(!n) return 0;
        vector<int> dp(n,0);
        int Max=0;
        //以i结尾的有效括号子串长度
        for(int i=1;i<n;i++)
        {
            if(s[i]==')'&&s[i-1]=='(') {
                if(i>=2)
                    dp[i]=dp[i-2]+2;
                else dp[i]=2;
            }
            if(s[i]==')'&&s[i-1]==')'&&i-dp[i-1]-1>=0&&s[i-dp[i-1]-1]=='(')
                if(i-dp[i-1]-2>=0) dp[i]=dp[i-1]+2+dp[i-dp[i-1]-2];
                else dp[i]=dp[i-1]+2;
                
            if(dp[i]>Max) Max=dp[i];
        }
        return Max;
    }
};
//简洁写法
class Solution {
public:
    int longestValidParentheses(string s) {
        int n=s.size();
        if(!n) return 0;
        vector<int> dp(n,0);
        int Max=0;
        //以i结尾的有效括号子串长度
        for(int i=1;i<n;i++)
        {
            if(s[i]==')'&&s[i-1]=='(') 
                dp[i]= (i>=2?dp[i-2]+2:2);
            if(s[i]==')'&&s[i-1]==')'&&i-dp[i-1]-1>=0&&s[i-dp[i-1]-1]=='(')
                dp[i]=(i-dp[i-1]-2>=0?dp[i-1]+2+dp[i-dp[i-1]-2]:dp[i]=dp[i-1]+2);
            if(dp[i]>Max) Max=dp[i];
        }
        return Max;
    }
};

堆栈:关键在于要先将-1入栈
-1先入栈,遇到左括号,下标入栈;
遇到右括号弹出栈顶元素:
(1)若栈顶不为空,说明原先有左括号匹配,长度为右括号下标-当前栈顶的下标;
(2)若栈顶为空,说明该右括号无匹配,弹出的是-1,这时将该右括号下标入队,即从该下标开始往后计算匹配括号的长度;

class Solution {
public:
    int longestValidParentheses(string s) {
        stack<int> S;
        S.push(-1);
        int n=s.size();
        if(!n) return 0;
        int len=0;
        for(int i=0;i<n;i++)
        {
            if(s[i]=='(') S.push(i);
            else 
            {
                S.pop();
                if(!S.empty())
                    len=max(len,i-S.top());
                else S.push(i);   
            }
        }
        return len;
    }
};	

记录left和right+无额外空间占用
从左到右记录左括号和右括号的数量,相等则记录len,右大于左则左右括号数量归0;(该遍历能得到‘()))’类型的括号);
从右到左记录右括号和做括号的数量,相等则记录len,左大于右则左右括号归0;(该遍历能得到‘((()’类型的括号数量);

class Solution {
public:
    int longestValidParentheses(string s) {
        int n=s.length();
        int left=0;
        int right=0; //从左到右遍历,记录各个括号的数量
        int maxlen=0;
        for(int i=0;i<n;i++)
        {
            if(s[i]=='(') left++;
            else right++;
            if(left==right) maxlen=max(maxlen,left+right);
            if(right>left) left=right=0;
        }
        left=right=0;
        for(int i=n-1;i>=0;i--)
        {
            if(s[i]=='(') left++;
            else right++;
            if(left==right) maxlen=max(maxlen,left+right);
            if(right<left) left=right=0;
        }
        return maxlen;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值