【动态规划Day three - 最长有效括号】

822 阅读3分钟
"将一头蠢笨的猪夸奖一番,它也是可以爬上树去的 --洛克菲勒

323.jpg

本文旨在记录一些关于dp的训练题,如果你对动态规划不熟悉,望转到该篇

----

我是怎么向5岁侄女解释动态规划的?

----

刷题之路,任重而道远啊

什么题可以选择动态规划来做?

1.计数

  • 有多少种方式走到右下角
  • 有多少种方法选出k个数是的和是sum

2.求最大值最小值

  • 从左上角走到右下角路径的最大数字和
  • 最长上升子序列长度

3.求存在性

  • 取石子游戏,先手是否必胜
  • 能不能选出k个数使得和是sum

题目一

leecode 32. 最长有效括号

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

示例 1:

输入:s = "(()"

输出:2

解释:最长有效括号子串是 "()"

示例 2:

输入:s = ")()())"

输出:4

解释:最长有效括号子串是 "()()"

示例 3:

输入:s = ""

输出:0

提示:

0 <= s.length <= 3 * 104

s[i] 为 '(' 或 ')'

看了之前的文章,我们就四步走吧

1.1. 动态规划组成部分1:确定状态

简单的说,解动态规划的时候需要开一个数组,数组的每个元素f[i]或者f[i][j]代表什么,类似数学题中x, y, z代表什么

wc,你倒是说说怎么确定要用动态规划来做啊?

  • 看题目,需要逐步验证最长长度
  • 没有时间复杂度空间复杂度限制,你可以选择>=On的
  • 跟前面题很类似,这里需要考虑生成括号的情况。
  • 尝试根据步骤写出转移方程

在这道题中,我们定义d[i]表示以下标 ii字符结尾的最长有效括号的长度

解动态规划需要两个意识:

  • 最后一步
  • 子问题

最后一步

我们用下图来讲解,i作为最后一个括号判断,我们只对为左括号做判断,左括号分两种情况,具体看子问题拆分。 lecoode 32.jpg

子问题

第一种情况为: ...()

因为括号前面可能还有有效的括号,之前我们定义了d[i] 表示下标i字符结尾的最长有效括号的长度,所以可以推导出:

d[i] = d[i-2] + 2

第二种情况为: ...))

321.jpg

如图,下标为2:i - d[i-1] -1

提出一个疑问:在下标2和5之前可能存在多个有效括号,其实都是d[i-1],因为我们定义的:d[i-1] 表示下标i-1字符结尾的最长有效括号的长度

322.jpg

最长有效括号的长度 :

d[i] = x + y

这里x = d[i - d[i-1] -2]

这里y = d[i-1] + 2

因此 :d[i] = d[i - d[i-1] -2] + d[i-1] + 2

1.2. 动态规划组成部分2:转移方程

d[i] 表示下标i字符结尾的最长有效括号的长度

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

1.3. 动态规划组成部分3:初始条件和边界情况

跟之前有些题一样,我们需要判断第i字符去判断第i-1个字符,所以i从1开始遍历,判断数组i-2需要考虑越界。

① ...() : i >=2

② ...)) : i - d[i-1] > 0 对应 :())

1.4. 动态规划组成部分4:计算顺序

从左往右

它的时间复杂度是On,空间复杂度也是On

当然也有其他的解决办法如栈

参考代码

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

@Test
public void isLongestValidParentheses() {
    String s = "()(())";
    longestValidParentheses(s);
}

热门推荐:

文末福利,最近整理一份面试资料《Java面试通关手册》,覆盖了Java核心技术、JVM、Java并发、SSM、微服务、数据库、数据结构等等。获取方式:GitHub github.com/Tingyu-Note…,更多内容陆续奉上。