LeetCode032:最长有效括号

要求:给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
1、动态规划
思路和算法

我们定义dp[i] 表示以下标 i 字符结尾的最长有效括号的长度。我们将 dp 数组全部初始化为 0 。显然有效的子串一定以‘)’ 结尾,因此我们可以知道以 ‘(’ 结尾的子串对应的dp 值必定为 0 ,我们只需要求解‘)’ 在 dp 数组中对应位置的值。
我们从前往后遍历字符串求解 dp 值,我们每两个字符检查一次:
s[i] =‘)’ 且 s[i - 1] ==‘(’,也就是字符串形如 “……()”,我们可以推出: dp[i]=dp[i−2]+2

我们可以进行这样的转移,是因为结束部分的 “()” 是一个有效子字符串,并且将之前有效子字符串的长度增加了 2 。
s[i] =‘)’ 且 s[i - 1] ==‘)’,也就是字符串形如 “……))”“……))”,我们可以推出:
如果 s[i -dp[i - 1] - 1] =‘(’,那么 dp[i]=dp[i−1]+dp[i−dp[i−1]−2]+2

我们考虑如果倒数第二个 ‘)’ 是一个有效子字符串的一部分,对于最后一个 ‘)’ ,如果它是一个更长子字符串的一部分,那么它一定有一个对应的‘(’ ,且它的位置在倒数第二个 ‘)’ 所在的有效子字符串的前面是 ’(’ ,也就是再加上dp[i−dp[i−1]−2]。
最后的答案即为dp 数组中的最大值。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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;
}

时间复杂度: O(n),其中 n 为字符串的长度。我们只需遍历整个字符串一次,即可将 dp 数组求出来。
空间复杂度: O(n)。我们需要一个大小为n的dp数组。


2、正向逆向结合
从左到右扫描,用两个变量 left 和 right 保存的当前的左括号和右括号的个数,都初始化为 0 。
如果左括号个数等于右括号个数了,更新合法序列的最长长度;左括号个数大于右括号个数了,向右边继续扫描。
如果左括号数目小于右括号个数了,那么后边无论是什么,此时都不可能是合法序列了,此时 left 和 right 归 0,然后接着扫描。
从左到右扫描完毕后,同样的方法从右到左再来一次,因为类似这样的情况((()),如果从左到右扫描到最后,left = 3,right = 2,期间不会出现 left == right。
但是如果从右向左扫描,扫描到倒数第二个位置的时候,就会出现 left = 2,right = 2 ,就会得到一种合法序列。

public int longestValidParentheses(String s) {
    int left = 0,right = 0,maxlength = 0;  //左括号,右括号,最大长度
    for (int i = 0; i < s.length(); i++) {
        if (s.charAt(i) == '('){
            left++; 
        }else {
            right++; 
        }
        if (left == right){ //匹配的时候
            maxlength = Math.max(maxlength,2 * right); //2*right -->2*右括号个数
        }else if(right > left){  //右括号数量大于左括号数量,重置左右括号的数量
            left = right = 0;
        }
    }
    
    left = right = 0;
    for (int i = s.length()-1; i > 0 ; i--) { //下标所以减1
        if (s.charAt(i) == '('){
            left++;
        }else {
            right++;
        }
        if (left == right){
            maxlength = Math.max(maxlength,2 * left);
        }else if(left > right){
            left = right = 0;
        }
    }
    return maxlength;
}

时间复杂度: O(n),其中 n 为字符串长度。我们只要正反遍历两边字符串即可。
空间复杂度: O(1)。我们只需要常数空间存放若干变量。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值