这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战
这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。
153. 最长有效括号 (longest-valid-parentheses)
标签
- 动态规划
- 困难
题目
给你一个只包含 '('
和 ')'
的字符串,找出最长有效
(格式正确且连续)括号子串的长度。
示例 1
输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
示例 2
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
示例 3
输入:s = ""
输出:0
基本思路
又遇到这种最优问题,还是想到用动规
方法解决。还是之前的一般套路
- 寻找最优子结构(状态表示)
- 归纳状态转移方程(状态计算)
- 边界初始化
1. 状态表示
首先是状态表示,关注点就是题目的目标
,从目标来找需要我们表示的变量
目标是: 最长有效
括号长度, 那么我们就定义 dp[i],就是s
的下标从 0——i
的子串中最长有效括号长度,或者说以s[i]
结尾的子串的最长有效括号长度。这样当i到s尾部时,就是整个s的 最长有效
括号长度。
一般字符串问题,用动态规划,都是这样的思路。
2. 状态转移
这个问题的转移其实需要看最后几位括号的情况:
最后一个字符是 s[i]的子串, 只有2种可能分别是是 '(' 或 ')', 如果是 '(', 则dp(i) = 0
。因为以'('作为结尾,必然不是有效的。所以我们只需要考虑 s[i] === ')' 的情况
s[i] === ')'
前提下,我们呢思考 s[i-1]
和最后一位能不能配对来分情况。
-
可配对时:
s[i-1] === (
,dp[i] = dp[i - 2] + 2 当然 i >= 2, 否则就是这对()
的长度直接为2
-
不可配对时:
s[i-1] === )
,以 s[i-1] 为结尾的最长有效长度为 dp[i-1], 最后两个字符已知是 '))', 我们看下面图
i-dp[i-1]-1 i(尾部)
| |
-----------------------
... ( (... ) )
-----------------------
| |
i-dp[i-1]-2 i-1
s[i-dp[i-1]-1]
必须是 '(', 才能和最后一个括号 ')' 匹配上。
那么此时转移方程可以写 dp[i] = dp[i-1] + dp[i-dp[i-1]-2] + 2
3. 边界初始化
再强调下,一般边界都是从 0 开始
的
dp[0] = 0
一个括号肯定是无效
接下来就是代码转换过程,注意边界条件即可
写法实现
const longestValidParentheses = (s) => {
let maxLen = 0
let dp = new Array(s.length).fill(0)
for (let i = 1; i < s.length; i++) {
// 最后一位只有 ')' 才可能有效
if (s[i] == ')') {
// 能匹配倒数第二位
if (s[i - 1] == '(') {
dp[i] = 2
// 如果前面还有, 加上 dp[i - 2]
if (i - 2 >= 0) {
dp[i] = dp[i-2] + 2;
}
} else if (s[i - dp[i - 1] - 1] == '(') {
// i - dp[i - 1] - 1 索引前面还有元素可成对
if (i - dp[i-1] - 2 >= 0) {
dp[i] = dp[i-1] + dp[i-dp[i-1]-2] + 2;
} else {
// 直接就是 dp[i-1] 加上最后的 )和 idx为 i - dp[i - 1] - 1 组成的一对
dp[i] = dp[i-1] + 2;
}
}
}
// 跟历史最大比较
maxLen = Math.max(maxLen, dp[i]);
}
return maxLen;
};
另外向大家着重推荐下这个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列
今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友
Or 搜索我的微信号infinity_9368
,可以聊天说地
加我暗号 "天王盖地虎" 下一句的英文
,验证消息请发给我
presious tower shock the rever monster
,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧