leetcode32:最长有效括号

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

示例 1:

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

两种做法:动态规划+栈

动态规划解法

dp数组的含义:已i为结尾的最长的有效括号的长度,分情况匹配前一个或者更早的'(',构建状态转移关系,租补累加到最终的最长的长度

以'('结尾的dp数组,统统赋值为0,以')'结尾的dp数组,需要按情况分析

两种情况的分析

1. 当i-1位置的符号是'('
  • 这意味着当前位置i')'和前一个位置i-1'('可以形成一个有效的括号对,长度为2。
  • 如果i的索引大于或等于2(即i >= 2),则说明当前位置前面可能有已经匹配的括号对,我们需要跳过这些括号对。
  • 因此,dp[i] = dp[i-2] + 2,即跳过前一个'('并加上当前的')'和前面的括号对。
2. 当i-1位置的符号是)时:
  • 这种情况更复杂,因为我们需要继续往前查找,看是否存在一个'('来和当前位置的')'匹配。

例子如下,当i=5时,i-1=4,需要去前面(i=2)的位置继续找有没有匹配的,不能直接赋值为0

        0 1 2 3 4 5
字符:  ( ) ( ( ) )

当i-1符号依然为')'时,而且i-dp[i-1]-1的位置的符号为‘(’ (也就是说第i位置的')'能和i-dp[i-1]-1位置的'('匹配上,只不过要跳过i前面的已经匹配完成的括号对')',dp转移公式为

dp[i]=dp[i-1]+2+dp[i-dp[i-1]-2]

让我们逐步分析这段转移公式

dp[i-dp[i-1]-2]是干什么的?

是获取与i的右括号匹配的i-dp[i-1]-1位置的左括号的前面的已经计算完成的括号对

dp[i-1]就是获取i前面的已经计算完成的括号对

那dp【i-1】不会包含了dp[i-dp[i-1]-2]吗?

关键在于,当i位置字符为‘(’时,该位置dp数组元素直接设置为0,也就是说,在dp[i-dp[i-1]-2]与dp[i-1]之间多出来的‘(’括号,也就是我们用于与i位置匹配的左括号,将dp[i-dp[i-1]-2]与dp[i-1]隔断了,中间是设置过为0的

解释完成,放出完整的java代码

class Solution {
    public int longestValidParentheses(String s) {
        int len=s.length();
        int[] dp=new int[len];
        //开始计数
        int maxCount=0;
        for(int i=1;i<len;i++){
            if(s.charAt(i)==')'){
                if(s.charAt(i-1)=='('){
                    //需要判断当前位置是不是大于2
                    if(i>=2){
                        dp[i]=dp[i-2]+2;
                    }else{
                        //如果是开头
                        dp[i]=2;
                    }
                }
                else if(s.charAt(i-1)==')'){
                    //需要往前判断是不是左括号
                    if(i-dp[i-1]-1>=0){
                        //首先判断位置够不够
                        //随后判断该位置是不是左括号
                        if(s.charAt(i-dp[i-1]-1)=='('){
                            //前一个位置以及匹配的左括号的前一个位置
                            dp[i]=dp[i-1]+2+((i-dp[i-1]-2>0)?dp[i-dp[i-1]-2]:0);
                        }
                    }
                }
            }
            maxCount=Math.max(maxCount,dp[i]);
        }
        return maxCount;
    }
}

栈写法

做法:

匹配过程

  • 用栈 stack 来存储左括号 '(' 的下标。当遇到右括号 ')' 时,从栈中弹出一个左括号的下标进行匹配。如果能弹出,则说明当前这一对括号是有效的。

标记匹配的括号

  • 当匹配成功时,在一个数组 tmp 中标记当前左右括号为匹配位置。具体来说,当匹配时,将当前右括号和对应左括号的 tmp 数组位置设为 1,表示这两个括号是有效配对。

计算最大有效子串长度

  • 遍历 tmp 数组,统计连续的 1 的个数(表示连续有效的括号对)。每次遇到 1 时,累加 tmpCount,并更新 count 为当前 tmpCount 和最大值之间的较大者。如果遇到 0,说明有效括号子串断开,重置 tmpCount

class Solution {
    public int longestValidParentheses(String s) {
        //使用栈解决试试
        //如果是左括号。就放入栈
        //如果是右括号,尝试从栈中弹出左括号
        //如果可以弹出,子串长度+2,如果栈为空,当前统计归0,跳过当前元素
        ArrayDeque<Integer> stack=new ArrayDeque<>();
        int len=s.length();
        int tmpCount=0;
        int count=0;
        int[] tmp=new int[len];
        for(int i=0;i<len;i++){
            if(s.charAt(i)=='('){
                stack.push(i);
            }
            else{
                if(!stack.isEmpty()){
                    //如果不是空的
                    int j=stack.pop();
                    tmp[i]=1;
                    tmp[j]=1;
                }
            }
        }
        for(int i=0;i<len;i++){
            if(tmp[i]==1){
                tmpCount++;
                count=Math.max(count,tmpCount);
            }
            else{
                tmpCount=0;
            }
        }
        return count;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值