本文旨在记录一些关于dp的训练题,如果你对动态规划不熟悉,望转到该篇
----
----
刷题之路,任重而道远啊
什么题可以选择动态规划来做?
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作为最后一个括号判断,我们只对为左括号做判断,左括号分两种情况,具体看子问题拆分。
子问题
第一种情况为: ...()
因为括号前面可能还有有效的括号,之前我们定义了d[i] 表示下标i字符结尾的最长有效括号的长度,所以可以推导出:
d[i] = d[i-2] + 2
第二种情况为: ...))
如图,下标为2:i - d[i-1] -1
提出一个疑问:在下标2和5之前可能存在多个有效括号,其实都是d[i-1],因为我们定义的:d[i-1] 表示下标i-1字符结尾的最长有效括号的长度
最长有效括号的长度 :
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…,更多内容陆续奉上。