前端算法必刷题系列[83]

168 阅读3分钟

这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战

这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。

153. 最长有效括号 (longest-valid-parentheses)

标签

  • 动态规划
  • 困难

题目

leetcode 传送门

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

示例 1

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

示例 2

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

示例 3

输入:s = ""
输出:0

基本思路

又遇到这种最优问题,还是想到用动规方法解决。还是之前的一般套路

  1. 寻找最优子结构(状态表示)
  2. 归纳状态转移方程(状态计算)
  3. 边界初始化

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,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧

参考