返回
Featured image of post Leetcode 32 最长有效括号

Leetcode 32 最长有效括号

三种,我有三种办法可以做你

最长有效括号

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


栈:存储左括号坐标

  1. 初始化
    • 创建一个栈 stk,用于存储未匹配的左括号的下标。
    • 初始化结果变量 res = 0,表示当前最长有效括号子串的长度。
    • 初始化变量 l = 0,表示当前理想情况下有效子串的起始位置。
  2. 遍历字符串
    • 从左到右遍历字符串 s,用变量 r 表示当前字符的下标(右指针)。
    • 对于每一个字符 s[r],执行以下操作:
  3. 处理左括号 (
    • 如果当前字符是左括号 (,将其下标 r 压入栈 stk 中。
  4. 处理右括号 )
    • 如果当前字符是右括号 )
    • 情况 1:如果栈 stk 为空,表示当前右括号没有匹配的左括号。
      • 更新 l = r + 1,表示新的理想起始位置。
    • 情况 2:如果栈 stk 不为空,表示当前右括号与最近的左括号匹配成功。
      • 弹出栈顶元素(匹配的左括号的下标)。
      • 如果栈 stk 为空,说明从 lr 的整个子串都是有效的。
        • 计算有效子串长度:r - l + 1
      • 如果栈 stk 不为空,说明栈中还有未匹配的左括号,有效子串的起始位置是栈顶元素的下一个位置。
        • 计算有效子串长度:r - stk[-1]
      • 更新 res = max(res, 当前有效子串长度)
  5. 返回结果
    • 遍历结束后,返回 res 的值,即最长有效括号子串的长度。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Solution:
    def longestValidParentheses(self, s: str) -> int:
        # stk 用于存储左括号的下标。通过栈来跟踪未匹配的左括号的位置。
        stk = []
        
        # res 用于记录最长有效括号子串的长度
        # l 是当前最长有效括号子串的起始位置
        res, l = 0, 0
        
        # 遍历字符串 s 的每一个字符,r 是当前字符的下标(右指针)
        for r in range(len(s)):
            # 如果当前字符是左括号 '(',将其下标压入栈中
            # 这将帮助我们后续匹配右括号
            if s[r] == '(':
                stk.append(r)
            
            # 如果当前字符是右括号 ')',并且栈为空,表示没有未匹配的左括号
            # 说明从 l 到 r 的这段字符串无法形成有效括号子串
            # 因此,将 l 更新为 r + 1,表示新的理想起始位置
            elif not stk:
                l = r + 1
            
            # 如果当前字符是右括号 ')',并且栈不为空,表示有未匹配的左括号
            # 此时,弹出一个左括号的下标,表示这个右括号与最近的左括号匹配成功
            else:
                stk.pop()
                
                # ll 表示当前有效括号子串的起始位置
                # 如果栈为空,表示从 l 到 r 的整个字符串都是有效的
                # 如果栈不为空,表示栈中还有未匹配的左括号
                # 因此有效子串的起始位置是栈顶元素的下一个位置
                ll = stk[-1] + 1 if stk else l
                
                # 更新最长有效括号子串的长度
                # 取当前结果和新的有效子串长度的最大值
                res = max(res, r - ll + 1)
        
        # 最终返回最长有效括号子串的长度
        return res

动态规划:当前坐标有效括号长度

  1. dp[i] 的定义
    • dp[i] 表示以 s[i] 结尾的最长有效括号子串的长度。
    • 通过动态规划逐字符计算,最终取最大值。
  2. j 的作用
    • 当当前字符是右括号 ),且前一个字符也是右括号 ) 时,j 用于找到可能匹配的左括号的位置。
    • 计算公式:j = i - 1 - dp[i - 1],即跳过当前已匹配的子串长度。
  3. 四种情况
    • 情况 1:当前字符是左括号 ( 或第一个字符(无法形成有效括号),跳过。
    • 情况 2:当前字符是右括号 ),且前一个字符是左括号 (,直接形成一对有效括号。
    • 情况 3:当前字符是右括号 ),且前一个字符是右括号 ),但 j < 0(没有匹配的左括号),跳过。
    • 情况 4:当前字符是右括号 ),且前一个字符是右括号 ),且 s[j] 是左括号 (,形成嵌套的有效括号。
  4. 返回值
    • 返回 dp 数组中的最大值,即最长有效括号子串的长度。
    • 如果 dp 为空,返回 0。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Solution:
    def longestValidParentheses(self, s: str) -> int:
        # dp[i] 表示以 s[i] 结尾的最长有效括号子串的长度
        dp = [0] * len(s)
      
        # 如果字符串为空,直接返回 0
        if not s:
            return 0
      
        # 遍历字符串的每一个字符
        for i in range(len(s)):
            # j 是可能匹配的左括号的位置
            # j = i - 1 - dp[i - 1],即跳过当前已匹配的子串长度
            j = i - 1 - dp[i - 1]
          
            # 1. 如果当前字符是左括号 '(' 或者 i == 0(第一个字符),跳过
            if i == 0 or s[i] == '(':
                continue
          
            # 2. 如果当前字符是右括号 ')',且前一个字符是左括号 '('
            # 此时可以直接形成一对有效括号,长度为 2
            # 同时,若 i > 1,还需加上 dp[i-2](前一个有效子串的长度)
            elif s[i - 1] == '(':
                dp[i] = 2 + (dp[i - 2] if i > 1 else 0)
          
            # 3. 如果当前字符是右括号 ')',且前一个字符是右括号 ')'
            # 此时需要找到匹配的左括号的位置 j
            # 如果 j < 0,表示没有匹配的左括号,跳过
            elif j < 0:
                continue
          
            # 4. 如果当前字符是右括号 ')',且 s[j] 是左括号 '('
            # 此时可以形成一对有效括号,长度为 2 + dp[i-1](中间的有效子串长度)
            # 同时,若 j > 0,还需加上 dp[j-1](前面的有效子串长度)
            elif s[j] == '(':
                dp[i] = 2 + dp[i - 1] + (dp[j - 1] if j > 0 else 0)
      
        # 返回 dp 数组中的最大值,即最长有效括号子串的长度
        return max(dp) if dp else 0

线性扫描:利用计数器维系有效性

  1. 有效括号子串的性质
    • 一个有效括号子串必须满足:
      • 左括号 ( 和右括号 ) 的数量相等
      • 在任何前缀中,左括号的数量不少于右括号的数量
      • 在任何后缀中,右括号的数量不少于左括号的数量
  2. 单次扫描的局限性
    • 从左到右扫描时,只能保证在任何后缀中右括号的数量不多于左括号的数量
    • 从右到左扫描时,只能保证在任何前缀中左括号的数量不多于右括号的数量
  3. 两次扫描的互补性
    • 从左到右扫描可以覆盖右括号用完的情况
    • 从右到左扫描可以覆盖左括号用完的情况
    • 结合两次扫描,可以确保所有有效括号子串都被统计到
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Solution:
    def longestValidParentheses(self, s: str) -> int:
        res = 0  # 最终结果,存储最长有效括号子串的长度
        
        # 第一次扫描:从左到右
        # cnt:计数器,用于记录左括号和右括号的差值
        # l:左指针,表示当前子串的起始位置
        l, r, cnt = 0, 0, 0
        for r in range(len(s)):
            # 每遇到左括号 `(`,cnt += 1;每遇到右括号 `)`,cnt -= 1
            cnt += int(s[r] == '(') - int(s[r] == ')')
            
            # 如果 cnt == 0,说明当前子串是一个有效括号子串
            # 更新结果为当前子串长度 (r - l + 1) 和当前结果中的较大值
            if not cnt:
                res = max(res, r - l + 1)
            
            # 如果 cnt < 0,说明当前子串无效(右括号多于左括号)
            # 重置左指针和计数器
            if cnt < 0:
                l, cnt = r + 1, 0
        
        # 第二次扫描:从右到左
        # cnt:计数器,用于记录右括号和左括号的差值
        # r:右指针,表示当前子串的结束位置
        l, r, cnt = len(s) - 1, len(s) - 1, 0
        for l in range(len(s) - 1, -1, -1):
            # 每遇到右括号 `)`,cnt += 1;每遇到左括号 `(`,cnt -= 1
            cnt += int(s[l] == ')') - int(s[l] == '(')
            
            # 如果 cnt == 0,说明当前子串是一个有效括号子串
            # 更新结果为当前子串长度 (r - l + 1) 和当前结果中的较大值
            if not cnt:
                res = max(res, r - l + 1)
            
            # 如果 cnt < 0,说明当前子串无效(左括号多于右括号)
            # 重置右指针和计数器
            if cnt < 0:
                r, cnt = l - 1, 0
        
        return res

Licensed under CC BY-NC-SA 4.0
© 2023 - 2025 壹壹贰捌· 0Days
共书写了258.6k字·共 93篇文章 京ICP备2023035941号-1