动态规划算法06-Longest Valid Parentheses问题

本文介绍了一种使用动态规划解决最长合法匹配子串长度问题的方法。针对仅包含括号的字符串,通过定义状态转移方程来寻找最长匹配子串的长度。文章提供了详细的解析过程及Python代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最长合法匹配子串长度(动态规划解法)

简述

- 给定一个只包含"(",")"的字符串,是判断一个最长的且能完全匹配的子串的长度。看到这种最有解问题,本能应该想到是动态规划解题。(且只求最值不求序列的可以递归实现)这是LeetCode动态规划类别的一道题。
- 

问题描述

给定一个字符串,只包含"(“和”)“,规定一个”(“必须匹配一个”)“,找一个最长的可以完全匹配的子串长度。如”((()“答案为2,”(()))"答案为4。

问题分析

如何找到最优子结构以及状态转移函数和边界是动态规划的核心思路。如何用已知最优解去构造当前最优解,这就是这题的思路。很明显,这是要填一个一维表。

不妨定义F(n)为以第n个字符为结尾的最大子串的长度。可以简单分析一下两种情况。

  • 第n个字符是"(“,不存在某个匹配子串结尾是”(",dp记为0。
  • 第n个字符是")",此时需要看前面一个字符。
    • 若s[n-1]为"(",那么两者刚好匹配,最长长度为这两者的长度2加上n-2为结尾的最长子串长度。(简单理解,刚好衔接)
    • 若s[n-1]为")“,那么就要去看以这个右括号为结尾的最长子串的前一个字符j是什么,如果j是”(“,那么刚好,这个”(“和n位置上的”)“包裹了一个最长子串,这是最长子串就是包裹的这个子串加以包裹起始的”(“前一个字符为结尾的字符串长度,此长度为2+dp[n-1]+dp[n-1-dp[n-1]-1];如果j是”)",衔接断开,dp[n]保持0即可。

F(n)边界、状态转移函数如下。
- F(n) = 0 ,s[n]=“(”
- F(n) = F(n-2) + 2 ,s[n-1]=“(”
- F(n) = 0 ,s[n-1]=“)”,s[i-1- dp[i - 1]]=“)”
- F(n) = 2 + F[i - 1] + F[i-1-dp[i-1]-1] ,s[n-1]=“)”,s[i-1- dp[i - 1]]=“(”

代码

注意:Python的列表可以负号索引,需要注意控制边界。代码使用循环填表,由底向上填表实现。(也可以递归实现)代码使用LeetCode答题基本格式。

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        if len(s) < 2:
            return 0

        dp = [0 for i in range(len(s))]
        for i in range(len(s)):
            if s[i] == "(":
                pass
            else:
                if i-1 >= 0:
                    if s[i - 1] == "(":
                        # 此时和i号字符匹配的已经找到
                        dp[i] = dp[i - 2] + 2
                    else:
                        # 此时这个右括号可能是一个合法子串的结尾也可能不是
                        if s[i-1- dp[i - 1]] == "(" and i-1-dp[i-1] >=0:
                            dp[i] = 2 + dp[i - 1] + dp[i-1-dp[i-1]-1]
                        else:
                            pass
        return max(dp)

补充说明

具体代码可以查看我的Github,欢迎Star或者Fork。此题为LeetCode动态规划的一题,题号为32,难度Hard。

### 回答1: 使用动态规划解决最长括号匹配问题的代码如下:int lengthOfLongestValidParentheses(string s) { int n = s.length(), maxLen = 0; vector<int> dp(n, 0); for (int i = 1; i < n; i++) { if (s[i] == ')') { if (s[i - 1] == '(') { dp[i] = (i - 2 >= 0 ? dp[i - 2] : 0) + 2; } else if (i - dp[i - 1] - 1 >= 0 && s[i - dp[i - 1] - 1] == '(') { dp[i] = dp[i - 1] + 2 + ((i - dp[i - 1] - 2 >= 0) ? dp[i - dp[i - 1] - 2] : 0); } maxLen = max(maxLen, dp[i]); } } return maxLen; } ### 回答2: 以下是用动态规划算法求最长括号匹配问题的代码: ```python def longest_valid_parentheses(s: str) -> int: n = len(s) if n == 0: return 0 dp = [0] * n ans = 0 for i in range(1, n): if s[i] == ')': if s[i - 1] == '(': if i >= 2: dp[i] = dp[i - 2] + 2 else: dp[i] = 2 elif i - dp[i - 1] > 0 and s[i - dp[i - 1] - 1] == '(': if i - dp[i - 1] >= 2: dp[i] = dp[i - 1] + dp[i - dp[i - 1] - 2] + 2 else: dp[i] = dp[i - 1] + 2 ans = max(ans, dp[i]) return ans ``` 该算法使用动态规划来求解最长括号匹配问题。定义一个长度为n的dp数组,其中dp[i]表示以下标为i的字符结尾的最长有效括号子串的长度。遍历字符串s,当遇到'('时,不需要做任何计算;当遇到')'时,分为两种情况:如果前一个字符是'(',则dp[i] = dp[i-2] + 2;如果前一个字符是')',则需要判断前一个字符所对应的最长有效括号子串的前一个字符是否是'(',如果是,则dp[i] = dp[i-1] + dp[i-dp[i-1]-2] + 2。最后,取dp数组中的最大值即为最终的结果。算法的时间复杂度为O(n),空间复杂度为O(n)。 ### 回答3: 最长括号匹配问题可以使用动态规划算法来解决,具体代码如下: ```python def longestValidParentheses(s): n = len(s) if n < 2: return 0 dp = [0] * n # dp[i]表示以s[i]为结尾的最长括号长度 max_length = 0 for i in range(1, n): if s[i] == ')': if s[i-1] == '(': # 形如 "()" 的匹配 dp[i] = dp[i-2] + 2 if i >= 2 else 2 elif i - dp[i-1] > 0 and s[i - dp[i-1] - 1] == '(': # 形如 "))" dp[i] = dp[i-1] + dp[i - dp[i-1] - 2] + 2 if i - dp[i-1] >= 2 else dp[i-1] + 2 max_length = max(max_length, dp[i]) return max_length ``` 原理解释: 动态规划的思路是从左到右遍历字符串,维护一个dp数组来记录以当前字符为结尾的最长括号长度。 在遍历过程中,我们需要处理两种情况: 1. 形如 "()" 的匹配:如果当前字符是")",且前一个字符是"(",则可以形成一个有效的括号匹配,这时dp[i]的值可以由dp[i-2] + 2得到,也即前一个最长括号长度再加上当前匹配的长度2。 2. 形如 "))" - 如果当前字符是")",且前一个字符也是")",我们需要判断前一个最长括号匹配的前一个字符是否是"(",如果是,则可以形成一个有效的括号匹配,并且dp[i]的值可以由前一次最长括号长度dp[i-1]再加上到前一个有效括号匹配的前一个位置的最长括号长度dp[i - dp[i-1] - 2],再加上当前匹配的长度2得到。此时需要特别注意边界情况,即当i - dp[i-1] < 2时,没有前一个有效的括号匹配,所以直接加上当前匹配的长度2即可。 遍历结束后,返回dp数组的最大值,即为最长括号匹配的长度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周先森爱吃素

你的鼓励是我坚持创作的不懈动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值