最长有效括号

这是一篇关于解决括号问题的博客,主要讨论了两种方法:括号计数和栈的应用。通过两次遍历解决最长有效括号子串的问题,同时探讨了如何处理特殊情况如'('和')'的计数,以及 '*' 在有效括号字符串中的作用。文章提供了具体的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

32. 最长有效括号

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

示例 1:

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

示例 2:

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

来源:力扣(LeetCode)
这一题真的太有意思了,栈,动态规划,还有括号计数,都是很方便很巧妙的方法,但我只会最low的暴力求每个子串的最大长度,哈哈😭

(一)

先说说括号计数的方法吧,顾名思义就是计数左右括号出现的次数

这种方法需要两次遍历,一次左到右,还有一次右到左,两者的顺序无所谓

先说说思路:
(从左往右遍历的时候)
遍历的时候用两个变量L和R记录左右括号出现的次数,一旦R>L,就说明当前遍历到的子串无效,为什么呢?因为多出来了一个左括号的话,只要右边还有右括号,就可以和左括号配对,还是有可能有效的,但是如果多出来一个右括号,那就没救了,没人和你配对啊,一直是多余的

然后当可能有人会问,只要左括号等于右括号,就一定有效吗?
对于一个完成了的序列来说,这个判断条件自然不成立,
比如

))((

但是我们是在一个一个的遍历啊,一开始就是右括号的话,R不就大于L了吗,那么将这个点作为开头的可能性都被我们抛弃了,所以在L == R的时候是可以保证括号的有效性的

缺陷:
虽然计数很方便,但是有一个致命的缺陷,那就是忽略了 L一直大于R的情况,比如

(((((((()

很明显这一串的最长有效括号为2,即最后面的()但是计数只能在L == R的时候保证括号的有效性,即只有在L = = R的时候才敢记录长度,却又只敢在R > L是舍弃当前位置,于是遍历上面的序列时,一直不舍弃,但又一直不敢记录长度,于是直到最后,R也没有等于L,
直到最后也没有记录长度,一直为0

但是这种缺陷也很容易解决,只要再来一次从右往左的遍历就行了!这就是上面所说的为什么要遍历两次的原因。

从右往左遍历的话就解决了L 一直大于 R的情况,两次遍历,互相弥补缺点

右往左的遍历其实和左往右差不多,改一下遍历顺序,改一下跳过条件就ok了

代码:

class Solution {
public:
    int longestValidParentheses(string s) {
        int L = 0, R = 0, maxLength = 0;
        for(int i = 0;i<s.size();++i)//左往右
        {
            if(s[i]=='(')   ++L;//记录左括号
            else            ++R;//记录右括号
            //一旦两者相等,(就记录当前长度),不过依题意,我们要取最大长度
            if(L == R)      maxLength = max(maxLength,L+R);
            else if(R>L)    L = R = 0;//跳过
        }
        L = R = 0;//这一步别忘了,清空计数,为了方便阅读,我就没写在for里
        for(int i = s.size()-1;i>=0;--i)//改一下,右往左
        {
            if(s[i]=='(')   ++L;//一样的
            else            ++R;//一样的
            if(L == R)      maxLength = max(maxLength,L+R);//一样的
            else if(R<L)    L = R = 0;//改一下,
        }      
        return maxLength;  
    }
};

(二)

提到括号,就不得不提到栈,这一题当然也可以用栈来解决

但是普通的括号匹配都是匹配整个序列,看中间是否会出错,出错就表明不是有效序列,
这一题的要求却是求最大的有效序列的长度,必须改变思路,,不过核心思路是不变的:

左括号就入栈,右括号就看栈里是否有剩余的左括号,如果有,则将左括号弹出栈

在这一题里,我们要始终保持栈顶的元素为最后一个的单身狗(没有能与之配对的括号)的下标,怎么理解呢?
在序列中如果有效子序列一直连续,那每次匹配完后,栈里都是空的,因为没有单身狗,但总会有来捣乱的单身狗,比如

()(()

上面的序列中,第三个括号 ’ ( '就是来捣乱的单身狗

但是具体情况比较复杂,单身狗可能没有,也可能是男’ ( ‘,也可能是女’ ) ’
怎么办呢?因为我们从左往右遍历的,我们先默认男性’ ( ‘都是单身狗,直到他的女朋友’ ) ‘来了,我们就把他踢出单身狗队列,而那些没有女朋友的或者说女朋友没来的,就默认他们是单身狗,这是对’ ( ’

然后是’ ) ’ ,除非队列中没有男性’ ( ‘,不然我们都默认它不是单身狗,不对它进行处理,反正’ ) ‘的下标也不会入栈,而如果我们发现在场没人了,那么这个’ ) '就是单身狗了,入栈

而求有效括号的最大长度,也变成了求最后一个单身狗后边的情侣人数

用下方代码的逻辑来解释一下:

首先遇见男性就入栈

而一旦遇见女性,先判断还有没有剩下的单身狗

如果没有了,就说明这个女性是现存唯一单身狗,入栈

然后如果有单身狗,那就判断是男还是女,如果是女,因为括号不搞同性恋,所以入栈

最后,就只有一种情况了,有一个男单身狗,可以配对,那就把男的踢出去,弹出栈

配对之后还是有两种情况
(一)栈空
如果一个单身狗都不剩了,就说明整个序列到此为止一直是有效括号,长度为当前括号下标+1

res = max ( res, i + 1 );

(二)不空
栈不空,就说明这个序列被单身狗打乱了,只能用下标的差来算出当前有效括号的长度,照理来说用下标算长度都需要+1,但这里配对成功的男性都被踢出去了,st.top()不是有效括号的开头,是更前面的下标,所以不用 i - st.top() +1 ,直接

res = max ( res, i - st.top() );
class Solution {
public:
    int longestValidParentheses(string s) {
        stack<int>st;
        int res = 0;
        for(int i = 0;i<s.size();++i)
        {
           if(s[i]=='(')st.push(i);//默认男的都是单身狗,入栈
           else
           {
               if(st.empty() || s[st.top()]==')') st.push(i);//女单身狗的几种情况,入栈
               else //成功匹配
               {
                   st.pop();//匹配完弹出
                   if(st.empty())res = max(res,i+1);//如果是特殊情况,一直有效,长度就这样赋值
                   else res = max(res,i-st.top());//被打乱了就得用差值来求长度了
               }
           }
        }
        return res;
    }
};


678. 有效的括号字符串

给定一个只包含三种字符的字符串:( ,) 和 *,写一个函数来检验这个字符串是否为有效字符串。有效字符串具有如下规则:

任何左括号 ( 必须有相应的右括号 )。
任何右括号 ) 必须有相应的左括号 ( 。
左括号 ( 必须在对应的右括号之前 )。
*可以被视为单个右括号 ) ,或单个左括号 ( ,或一个空字符串。
一个空字符串也被视为有效字符串。

来源:力扣(LeetCode)

思路

沿用上一题的括号计数法,核心思路没啥变化

依旧是计数来判断当前遍历到的子串是否有效,如果无效,就加上*的数量,看看是不是有效,如果依旧无效,就说明没救了,直接return false吧

同理再反向遍历一次,同样计数+判断

上一题发现无效的时候就的抛弃当前位置,因为我们要找最长序列,这一题里发现无效的时候,可能还是有救的,因为有*,于是我们加上*看看有没有救

代码:

class Solution {
public:
    bool checkValidString(string s) {
        int L = 0,R = 0,ch = 0;
        int size = s.size();
        for(int i = 0;i<size;++i)
        {
            if(s[i]=='(')++L;
            else if(s[i]==')')++R;
            else if(s[i]=='*')++ch;
            if(R>L)
            {
                if(L+ch<R)//如果算上*的数量L依旧比R小,就说明没救了,不然依旧有救
                return false;
            }
        }
        L = 0,R = 0,ch = 0;
        for(int i = size-1;i>=0;--i)
        {
            if(s[i]=='(')++L;
            else if(s[i]==')')++R;
            else if(s[i]=='*')++ch;
            if(L > R)
            {
                if(R+ch<L)
                return false;
            }
        }
        return true;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timathy33

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值