32. 最长有效括号

159 阅读5分钟

【题目】

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

  示例 1:

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

示例 2:

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

示例 3:

输入: s = ""
输出: 0

提示:

  • 0 <= s.length <= 3 * 104
  • s[i] 为 '(' 或 ')'

【题目解析】

思路

动态规划法

动态规划是解决此问题的一种有效方法。其核心思想是使用一个数组dp来记录每个位置的最长有效括号长度,然后基于当前字符及其前面的字符来更新dp数组。

  1. 初始化:创建一个长度与输入字符串s相同的数组dp,所有元素初始化为0。

  2. 状态转移

    • 如果s[i]')'s[i-1]'(',形如"()",则dp[i] = dp[i-2] + 2
    • 如果s[i]')'s[i-1]')'s[i-dp[i-1]-1]'(',形如"(...))",则dp[i] = dp[i-1] + dp[i-dp[i-1]-2] + 2
  3. 计算最大值:遍历dp数组,找出最大值即为最长有效括号的长度。

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        # 初始化最长有效括号的长度为0
        max_length = 0
        # 创建一个和输入字符串s长度相同的数组dp,dp[i]表示以s[i]结尾的最长有效括号的长度
        dp = [0] * len(s)
        
        # 从第二个字符开始遍历字符串s
        for i in range(1, len(s)):
            # 如果当前字符是')',则尝试找到与之匹配的'('
            if s[i] == ')':
                # 如果前一个字符是'(',则形成了"()",这是最基本的有效括号
                if s[i-1] == '(':
                    dp[i] = (dp[i-2] + 2) if i >= 2 else 2
                # 如果前一个字符是')',并且根据dp[i-1]回溯到的位置是'(',则形成了"(...))"的有效括号
                elif i - dp[i-1] > 0 and s[i-dp[i-1]-1] == '(':
                    dp[i] = dp[i-1] + (dp[i-dp[i-1]-2] if (i-dp[i-1]) >= 2 else 0) + 2
                # 更新最长有效括号的长度
                max_length = max(max_length, dp[i])
        
        # 返回最长有效括号的长度
        return max_length

栈方法

另一种解法是使用栈。栈用来跟踪未匹配的括号的索引,从而能够计算有效括号的长度。

  1. 初始化:创建一个栈,并将一个特殊标记-1压入栈,作为基准点。

  2. 遍历字符串:对于每个字符:

    • 如果是'(',将其索引压入栈。
    • 如果是')',弹出栈顶元素。如果此时栈为空,将当前索引压入栈。否则,计算当前有效括号的长度为当前索引减去栈顶元素的索引。
  3. 更新最大长度:在遍历过程中,更新最长有效括号长度。

执行

动态规划

截屏2024-02-03 22.26.17.png

截屏2024-02-03 23.08.45.png

【总结】

“最长有效括号”问题是一个经典的字符串处理问题,它要求在给定包含左右括号的字符串中找到最长的有效(格式正确且连续)括号子串的长度。这个问题不仅在算法面试中频繁出现,也是字符串处理和动态规划领域的基本问题之一。

适用问题类型

动态规划和栈的方法适用于各种涉及字符串解析、验证和转换的问题,特别是那些需要识别和处理成对符号或标记的场景,例如:

  • 编程语言解析器中的括号匹配。
  • HTML或XML等标记语言的标签验证。
  • 表达式求值问题中的运算符和括号处理。
  • 寻找或验证字符串中特定模式或结构的问题。

解决算法

动态规划法

  • 核心思想是建立一个一维的DP数组,用于存储到每个字符为止的最长有效括号子串长度。通过分析当前字符与其前一字符的关系,更新DP数组,最终得到全局最长有效括号的长度。
  • 算法复杂度:时间复杂度为O(n),空间复杂度也为O(n),其中n是输入字符串的长度。

栈方法

  • 利用栈来跟踪未匹配括号的索引,通过栈的入栈和出栈操作来计算每个有效括号子串的长度,最终找到最长的那个。
  • 算法复杂度:时间复杂度为O(n),空间复杂度为O(n)。

算法选择和优化

  • 动态规划法适合于需要详细跟踪每个子问题解的场景,它提供了一种更为通用和系统的解决方案框架。
  • 方法则更直观且在某些情况下更易于实现,尤其是当问题可以通过成对消除或匹配的方式简化时。

扩展应用

掌握了这两种方法后,不仅可以解决“最长有效括号”问题,还能够扩展到其他复杂的字符串处理问题,如:

  • 多种括号的有效性验证(如包含{}[]()的情况)。
  • 带有通配符的字符串匹配问题。
  • 序列重建或序列化问题,其中需要根据一定规则对字符串或字符序列进行重排或验证。

结论

通过动态规划和栈的方法解决“最长有效括号”问题不仅展现了问题解决的多样性,也体现了算法设计中对问题本质的深入理解。这两种方法各有优势,在不同的问题场景和需求下,可以灵活选择和应用。更重要的是,深入学习这些算法和解题技巧可以为解决更广泛的算法问题奠定坚实的基础,提高解题的效率和质量。

题目链接

最长有效括号