LeetCode0032-最长有效括号

LeetCode0032-最长有效括号

题目:

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

示例 1:
输入: "(()"
输出: 2
解释: 最长有效括号子串为 "()"
示例 2:
输入: ")()())"
输出: 4
解释: 最长有效括号子串为 "()()"

分析:

动态规划(Solution1)

定义 dp[i] 表示以下标i字符结尾的最长有效括号的长度。将dp数组全部初始化为0。显然有效的子串一定以 ‘)’ 结尾,因此知道以 ‘(’ 结尾的子串对应的dp值必为0,只需要求解’)'在数组中对应位置的值。

我们从前往后遍历字符串求解dp值,每俩个字符检查一次:

  • s[i] = ‘)’ 且 s[i-1] = ‘(’ ,也就是字符串形如“……()”,可以推导出:
    d p [ i ] = d p [ i − 2 ] + 2 d p[i]=d p[i-2]+2 dp[i]=dp[i2]+2

可以进行这样的转移,因为结束部分的“()”是一个有效字符串,并且之前的有效字符串的长度增加了2。

  • s[i] = ‘)’ 且 s[i-1] = ‘)’,也就是形如“……))”,可以退出:
    如果s[i-dp[i-1]-1] = ‘(’ ,那么:
    d p [ i ] = d p [ i − 1 ] + d p [ i − d p [ i − 1 ] − 2 ] + 2 d p[i]=d p[i-1]+d p[i-d p[i-1]-2]+2 dp[i]=dp[i1]+dp[idp[i1]2]+2

考虑如果倒数第二个’)‘是一个有效字符串的一部分(记作sub),对于最后一个’)’,如果它是一个更长子字符串的一部分,那么它一定有一个对应的’(‘且它的位置在倒数第二个’)‘所在的有效字符串的前面(也就是sub的前面)。因此,如果子字符串sub的前面恰好是’(’,那么就用2 加上sub的长度(dp[i-1])去更新dp[i]。同时,也会把有效子串“(sub)”之前的有效子串的长度也加上,也就是再加上dp[i-dp[i-1]-2]。

最后的答案即为dp数组中的最大值。

栈(Solution02)

通过栈,可以在遍历给定字符串的过程中去判断到目前为止扫描的子串的有效性,同时能得到最长有效括号的长度。

具体做法是始终保持栈底元素为当前已经遍历过的元素,最后一个没有被匹配的右括号的下标,这样的做法主要是考虑了边界条件的处理,栈里其他元素维护左括号的下标:

  • 对于遇到的每个’(’,将它的下标放入栈中
  • 对于遇到的每个’)’,先弹出栈顶元素表示匹配了当前右括号:
    如果栈为空,说明当前的右括号为没有被匹配的右括号,将其下标放入栈中来更新之前的最后一个没有被匹配的右括号下标
    如果栈不为空,当前右括号的下标减去栈顶元素即为该右括号为结尾的最长有效的长度

从前往后遍历字符串并更新答案即可。
注意, 如果一开始栈为空,第一个字符为左括号的时候会将其放入栈中,这样不满足提到的[最后一个没有被匹配的右括号的下标],为了统一,在开始的时候先往栈中添加一个元素为-1。

代码:
/**
 * 0032-最长有效括号
 * 给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。
 * <p>
 * 示例 1:
 * <p>
 * 输入: "(()"
 * 输出: 2
 * 解释: 最长有效括号子串为 "()"
 * <p>
 * 示例 2:
 * <p>
 * 输入: ")()())"
 * 输出: 4
 * 解释: 最长有效括号子串为 "()()"
 */

import java.util.ArrayDeque;
import java.util.PriorityQueue;
import java.util.Queue;

/**
 * 动态规划
 */
class Solution01 {
    public int longestValidParentheses(String s) {
        // 保存最大结果
        int maxResult = 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;
                }
                maxResult = maxResult > dp[i] ? maxResult : dp[i];
            }
        }
        return maxResult;
    }
}

/**
 * 栈
 */

class Solution02 {
    public int longestValidParentheses(String s) {
        int maxResult = 0;
        /**
         * 当程序中需要使用 栈 这种数据结构是,推荐使用ArrayDeque,尽量避免使用 Stack,
         * 因为Stack 是古老的集合,性能较差
         *
         * 本程序中,使用到ArrayDeque 的方法:
         * push(E e) 栈顶添加一个元素
         * pop() 移除栈顶元素,如果栈顶没有元素抛出异常
         * peek()   取栈顶元素
         */
        ArrayDeque<Integer> stack = new ArrayDeque<>();
        stack.push(-1);
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                stack.push(i);
            } else {
                stack.pop();
                if (stack.isEmpty()) {
                    stack.push(i);
                } else {
                    maxResult = maxResult > i - stack.peek() ? maxResult : i - stack.peek();
                }
            }
        }
        return maxResult;
    }
}

/**
 * 测试类
 */
public class Study0032 {
    public static void main(String[] args) {
        System.out.println(new Solution02().longestValidParentheses("(()"));
    }
}

结果:

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值