32. 最长有效括号

判断有效括号

在这题之前要先了解“如何判断有效括号”,即将有效括号的判断,转化为元素的出入栈,并进一步用一个整型值来表示。这里有个引理,即遍历有效括号的过程中,出栈次数不可能大于入栈次数,整型值不可能为-1。即假如(代表1,)代表-1,不可能出现())(这样的情况为有效括号,其中s[0:3]的对应值为-1,s[4]为1。

官方题解理解

https://leetcode-cn.com/problems/longest-valid-parentheses/solution/zui-chang-you-xiao-gua-hao-by-leetcode-solution/
官方给出了简洁的题解,其中比较难理解的是第二个if块,i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(',这个意思相当于是说,s[i-dp[i-1]-1]到s[i]应当形成如同((...))的样式,其中(..)的长度可以是0。

class Solution {
    public int longestValidParentheses(String s) {
        int maxans = 0;
        int[] dp = new int[s.length()];
        for (int i = 1; i < s.length(); i++) {
            if (s.charAt(i) == ')') {
                if (s.charAt(i - 1) == '(') {
                    dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
                } else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
                    dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
                }
                maxans = Math.max(maxans, dp[i]);
            }
        }
        return maxans;
    }

第二个if块分析

为什么一定要i - dp[i - 1] > 0呢?首先,s[i-dp[i-1]-1]到s[i]可以组成的字符串有如下几种情况(|为字符串首部):

  1. 中间部分长度为0
    • i - dp[i - 1] > 0,比如|...),如果左侧添个(,就能形成有效括号。
    • i - dp[i - 1] <= 0 ,比如|),只有一个有括号,不能形成有效括号。
  2. 中间部分长度不为0
    • i - dp[i - 1] > 0,比如|...()),如果合起来的部分左侧再添个(,就能组成有效括号。
    • i - dp[i - 1] <= 0 ,比如|()),合起来的部分不能组成有效括号。为什么?

有人会疑惑2.2,当有效括号与一个右括号放在一起时,右括号有没可能与一部分字符串组成有效括号?比如下图

这是不可能的,用反证法可以证明。加入)与中间某一段组成了有效括号,那么设)为1,则中间那一段必须为-1,因为有效括号的整型值必须为0。同理,左边一段的整型值一定为+1。

而中间一段是不可能为-1的,因为我们看到中间段与左边段组成的有效括号,当遍历这段括号的过程中,整型值已经变成过一次-1,即小于0,出栈次数大于入栈次数,是不可能的。

所以,反证法证明2.2成立。

第二个if块总结

所以,合并条件可知,只要 i - dp[i - 1] > 0,并在左侧添一个(号,就可以形成有效括号了。再之后加上更左边一段的有效括号长度,即可得到完整的长度了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值