32. 最长有效括号

32. 最长有效括号

最长有效括号

dp数组含义

dp数组中 d p [ i ] dp[i] dp[i] 表示以下标 i i i 结尾的子串的最长有效括号的长度。如 ()()() 的 dp 数组为 [ 0 , 2 , 0 , 4 , 0 , 6 ] [0,2,0,4,0,6] [0,2,0,4,0,6] ()(()) 的 dp 数组为 [ 0 , 2 , 0 , 0 , 2 , 6 ] [0,2,0,0,2,6] [0,2,0,0,2,6]

状态转移公式

如果 s [ i ] s[i] s[i]( ,那肯定不可能组成有效数组, d p [ i ] = 0 dp[i]=0 dp[i]=0

如果 s [ i ] s[i] s[i]) ,就有机会了,我们再分情况来看,按照其前一个字符来分:

  • 如果 s [ i − 1 ] s[i-1] s[i1]( ,这种情况比较简单, s [ i − 1 ] s[i-1] s[i1] s [ i ] s[i] s[i] 组成了一个有效的独立的括号串,相当于将 d p [ i − 2 ] dp[i-2] dp[i2] 扩充了 2,即 d p [ i ] = d p [ i − 2 ] + 2 dp[i]=dp[i-2]+2 dp[i]=dp[i2]+2

这里要注意是 i ≥ 2 i\ge 2 i2 的情况,如果 i < 2 i<2 i<2 (其实就只有 i = 1 i=1 i=1),则 d p [ i ] = 2 dp[i]=2 dp[i]=2

  • 如果 s [ i − 1 ] s[i-1] s[i1]) ,就要复杂一点,这时最近的两个括号并不能组成有效括号串,想要整体组成有效的括号串,只有可能是分别跟更前面的两个 ( 组成。即类似:((xxx)) 。详情看图。

    这种情况我们要先确认与 i i i 处的 ) 对应的位置 i − d p [ i − 1 ] − 1 i-dp[i-1]-1 idp[i1]1 处是 ( ,否则肯定不匹配了,即确认 s [ i − d p [ i − 1 ] − 1 ] = ′ ( ′ s[i-dp[i-1]-1]='(' s[idp[i1]1]=( (当然首先要确认 i − d p [ i − 1 ] > 0 i-dp[i-1]>0 idp[i1]>0)。

    这时我们应该怎样更新此时的 d p [ i ] dp[i] dp[i] 呢,从图中可以看到, d p [ i ] dp[i] dp[i] 的值可能来自三部分:字符串最前面(可能存在)的一个独立的有效括号串,中间的有效括号串,和新增的一对括号即加2。因此 d p [ i ] = d p [ i − d p [ i − 1 ] − 2 ] + d p [ i − 1 ] + 2 dp[i]=dp[i-dp[i-1]-2]+dp[i-1]+2 dp[i]=dp[idp[i1]2]+dp[i1]+2 。注意当 i − d p [ i − 1 ] < 2 i-dp[i-1]<2 idp[i1]<2 时,最前面一部分不可能存在有效括号串,即第一项为零 d p [ i ] = d p [ i − 1 ] + 2 dp[i]=dp[i-1]+2 dp[i]=dp[i1]+2

在这里插入图片描述

dp数组初始化

全部初始化为 0 即可。

遍历顺序

后面的 dp 数组的取值依赖于前面的 dp 数组的值,因此从前向后遍历即可。注意 d p [ 0 ] dp[0] dp[0] 仅有一个字符,不可能组成有效括号,因此从下标 1 开始遍历即可。

代码

class Solution {
public:
    int longestValidParentheses(string s) {
        int n = s.size();
        vector<int> dp(n, 0);
        int ans = 0;
        for (int i=1; i<n; ++i) {
            if (s[i] == '(') continue;
            if (s[i-1] == '(') {
                if (i >= 2) dp[i] = dp[i-2] + 2;
                else dp[i] = 2;
            }
            else {
                if (i-dp[i-1]>0 && s[i-dp[i-1]-1]=='(') {
                    if (i-dp[i-1] >= 2) dp[i] = dp[i-1] + dp[i-dp[i-1]-2] + 2;
                    else dp[i] = dp[i-1] + 2;
                }
            }
            ans = max(ans, dp[i]);
        }
        return ans;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值