HOT100题打卡第27天——动态规划(hard)

hi大家!终于,终于,今天迎来了动态规划小结的最后一题,我们今天用一道hard暂别动态规划

先来看题目

32. 最长有效括号

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

左右括号匹配,即每个左括号都有对应的右括号将其闭合的字符串是格式正确的,比如 "(()())"

示例 2:

复制代码
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"

题目分析

1.解题思路

要配对括号,找右括号来凑左括号,和找左括号来凑右括号,思路基本都是一致的,因为有效的括号就是左括号在前,右括号在后,正向遍历字符串,如果先遇到左括号,暂时没有右括号和它配对,那这个位置的有效括号长度就赋值为0,往后遍历,当遍历到右括号时,就会往前找还未被配对的左括号

  • 如果前一个字符就是左括号,直接匹配(形成 ())。
  • 如果前一个字符是右括号,说明可能存在嵌套或连续的有效子串,需要跳过已匹配的范围,找更前面的左括号。

2.dp数组含义

我们写动态规划这类题,通常会借用dp数组,那这道题的dp数组的含义是什么呢,我们要分析到某个位置的有效括号的长度,因此 数组的索引 i 就对应字符串的索引,可以对应找到字符串中对应的元素,dp i 就表示以当前元素结尾的有效括号的长度

3.状态转移方程

前面分析解题思路我们知道,在遍历到右括号时,去前面找左括号需要分两种情况讨论

1.如果当前元素 i 的前一位就是左括号,那就恰好能匹配,匹配成的括号的有效长度是2,但是别忘了还要加上元素i-1前面那段字符串中括号的有效长度,就是到 i-2位置时,括号的有效长度

此时dp i = dp i-2 + 2

2.如果 i 的前一位 i - 1 仍然是右括号,那我们要找和 i 位置匹配的左括号,就应该跳过前面 i-1位置匹配过的有效括号长度,找到有效长度的前一个位置

int pre = dpi - dp\[i - 1 - 1]

判断这个位置是不是左括号,如果是就能匹配成功,如果这个位置还是右括号,说明 i 位置已经不可能匹配成功了,因为即使再往前还有左括号,因为中间还隔着有效长度的前一个位置上这个右括号

所以此时 dpi = dpi - 1 + pre + 2;

数组的初始化

因为一开始并不能知道每个位置上的匹配情况,所以dp数组先初始化为0,但因为数组本身默认值就为0,我们初始化无需进行更多操作

后面循环过程中更新有效值就可以了


好了,那题目分析到这里大家应该也能理解了,我们来看看代码是如何实现的

java 复制代码
class Solution {
    public int longestValidParentheses(String s) {

        //定义dp数组:索引i和字符串的索引i对应,指向当前元素,dp[i]表示以当前元素结尾的最大有效括号长度
        int[] dp = new int[s.length()];

        //数组默认初始值是0,全都先默认是无效状态

        //异常处理,如果字符串长度是0或者小于2,有效长度都是0,直接返回0
        if (s.length() == 0 || s.length() < 2) {
            return 0;
        }

        //仅有0索引位置元素是没办法找到有效括号的,至少都需要两个元素,所以索引直接从1开始
        for (int i = 1; i < s.length(); i++) {
           /* 不需要这个步骤,因为数组中所有的初始值本来就是0,我们只需要把有效部分的值更新就好了
           if(s.charAt(i)=='('){
                dp[i]=0;
            }*/

            //只考虑当前元素是 )的情况
            if (s.charAt(i) == ')') {
                //如果前一位刚好是 ( 能和当前元素组成有效的括号,但此时还要考虑前一位的前面有效括号的长度
                //但是这里为了避免索引越界异常,需要进行边界判断,如果当前元素i小于2,那么说明前面有效括号的长度是0,直接加0
                //如果大于等于2,则需要加上前面的有效括号长度
                if (s.charAt(i - 1) == '(') {
                    dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
                    //如果前一位仍然是 )怎么办
                } else if (s.charAt(i - 1) == ')') {
                    //需要跳过前一位的有效范围,因为前一位如果有匹配的 (,那这个范围内是不会再有(出现的,只能在这个有效范围的前面一位上找
                    //为什么是前一位不是前面的整个范围呢?
                    /*1.如果这个位置是左括号,说明能配对,形成更大的有效子串。
                      2. 如果这个位置是右括号,说明当前右括号无法配对(因为更左侧的位置即使有左括号,中间也隔着这个未配对的右括号,无法和 i 位置形成有效括号)*/
                    if (i - dp[i - 1] - 1 >= 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
                        //这里还是依然对边界进行了判断
                        //得到前面部分的有效长度
                        int pre = i - dp[i - 1] - 2 >= 0 ? dp[i - dp[i - 1] - 2] : 0;
                        //以i元素结尾的括号的有效长度=前面部分的有效长度+前一位的有效长度+2(i位置括号匹配,长度是2)
                        dp[i] = dp[i - 1] + pre + 2;
                    }
                }
            }


        }

        //最后获取dp数组中的最大值就是有效括号长度的最大值
        int max = dp[0];
        for (int i = 1; i < dp.length; i++) {
            max = Math.max(max, dp[i]);
        }

        return max;
    }
}

这题还有一点很重要,就是索引越界异常的判断,我代码里面标注的很清楚了

好了,那这道题就写到这里了,各位明天见!

相关推荐
羑悻的小杀马特1 小时前
【动态规划篇】正则表达式与通配符:开启代码匹配的赛博奇幻之旅
c++·算法·leetcode·正则表达式
吴可可1231 小时前
SolidWorks二次开发实战应用
算法
春日见2 小时前
5分钟入门强化学习之蒙特卡洛(MC)算法与实现
运维·服务器·人工智能·深度学习·算法·机器学习
x_xbx2 小时前
LeetCode:581. 最短无序连续子数组
算法·leetcode·排序算法
代码中介商2 小时前
排序算法完全指南(八):归并排序深度详解
数据结构·算法·排序算法
kkeeper~11 小时前
0基础C语言积跬步之数据在内存中的存储
c语言·数据结构·算法
wabs66612 小时前
关于贪心算法的一些自我总结【力扣45.跳跃游戏II】【灵感来源:代码随想录】
算法·贪心算法·复盘
2401_8769641312 小时前
【湖北专升本】2026湖北专升本真题PDF+备考资料汇总
数据结构·人工智能·经验分享·深度学习·算法·计算机视觉
嗝o゚13 小时前
CANN GE 算子融合——融合算法与调度策略
算法·昇腾·cann·ge