【力扣100题】50.最长有效括号

题目描述

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

左右括号匹配,即每个左括号都有对应的右括号将其闭合的字符串是格式正确的,例如 "(()())"

示例:

  • 输入:s = "(()" → 输出:2(最长有效括号子串是 "()")
  • 输入:s = ")()())" → 输出:4(最长有效括号子串是 "()()")
  • 输入:s = "" → 输出:0

解题思路总览

方法 思路 时间复杂度 空间复杂度
动态规划 dp[i] 表示以第 i 个字符结尾的最长有效括号长度 O(n) O(n)
栈中存索引,遇到 ) 时弹出并计算长度 O(n) O(n)
双指针 两遍扫描,分别从左到右和从右到左处理不匹配的情况 O(n) O(1)

本题采用动态规划方法。


完整代码

cpp 复制代码
class Solution {
public:
    int longestValidParentheses(string s) {
        int maxLen = 0, n = s.size();
        vector<int> dp(n, 0);
        for (int i = 1; i < n; i++) {
            if (s[i] == ')') {
                if (s[i - 1] == '(') {
                    dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
                } else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(') {
                    dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
                }
                maxLen = max(maxLen, dp[i]);
            }
        }
        return maxLen;
    }
};

算法流程图

复制代码
输入: s = ")()())"

初始化:
  n = 6
  dp[0...5] = 0
  maxLen = 0

i = 1, s[1] = '(':
  s[1] == ')'? 否,跳过
  maxLen = 0

i = 2, s[2] = ')':
  s[2] == ')'? 是
  s[1] == '('? 是!
    dp[2] = dp[0] + 2 = 0 + 2 = 2
  maxLen = max(0, 2) = 2

i = 3, s[3] = '(':
  s[3] == ')'? 否,跳过
  maxLen = 2

i = 4, s[4] = ')':
  s[4] == ')'? 是
  s[3] == '('? 是!
    dp[4] = dp[2] + 2 = 2 + 2 = 4
  maxLen = max(2, 4) = 4

i = 5, s[5] = ')':
  s[5] == ')'? 是
  s[4] == '('? 否,进入 else if
    i - dp[4] = 5 - 4 = 1 > 0
    s[1] = '('? 是!
    dp[5] = dp[4] + dp[0] + 2 = 4 + 0 + 2 = 6
  maxLen = max(4, 6) = 6

最终 maxLen = 6
输出: 6

逐行解析

cpp 复制代码
int maxLen = 0, n = s.size();

含义: maxLen 记录全局最长有效括号长度,n 记录字符串长度。

cpp 复制代码
vector<int> dp(n, 0);

含义: dp[i] 表示以第 i 个字符结尾的最长有效括号子串长度。初始化为 0。

cpp 复制代码
for (int i = 1; i < n; i++)

含义: 从索引 1 开始遍历(因为 dp0 只能是 0,不需要计算)。

cpp 复制代码
if (s[i] == ')')

含义: 只有当当前字符是 ) 时才可能形成有效括号。

cpp 复制代码
if (s[i - 1] == '(')

含义: 情况一:"..." 形式的最近匹配。例如 "()""(...)()"

cpp 复制代码
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;

含义: 如果是 "()" 形式,长度为 2 加上 dpi-2(之前已匹配的长度)。需要判断 i-2 是否 >= 0。

cpp 复制代码
else if (i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(')

含义: 情况二:类似 "(...])" 的形式,其中 ... 已经是有效括号段。例如 "(())" 中 s3=')' 时,i-dpi-1=3-2=1,s0='(',形成匹配。

cpp 复制代码
dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;

含义:

  • dp[i - 1]:之前已经匹配的有效括号长度
  • ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0):加上再之前的有效括号长度
  • + 2:加上当前匹配的 "()"
cpp 复制代码
maxLen = max(maxLen, dp[i]);

含义: 更新全局最长长度。


状态转移图解

复制代码
情况一:"()" 形式
        i-1   i
        '('   ')'
  dp[i] = dp[i-2] + 2

情况二:"(...])" 形式
             i-1      i
        "... ( ... ) )"
                 ^dp[i-1]
        ^
        i - dp[i-1] - 1 (与 i 配对的 '(' 的位置)
  dp[i] = dp[i-1] + dp[i - dp[i-1] - 2] + 2

复杂度分析

复杂度 说明
时间复杂度 O(n) 只需遍历一次字符串
空间复杂度 O(n) dp 数组大小为 n

面试追问 FAQ

问题 答案
为什么从 i = 1 开始而不是 i = 0 因为 dp0 一定是 0(空字符串无法形成有效括号),从 1 开始可以减少边界判断
dp[i] 为什么要定义为「以第 i 个字符结尾」? 这样可以利用 dpi-1 的值,实现 O(1) 转移。如果是「前 i 个字符中最长」,就无法利用之前的结果
栈方法是怎么工作的? 栈中存左括号的索引,遇到右括号时弹出。如果弹出后栈非空,当前长度 = i - 栈顶索引;如果栈空,当前长度 = i + 1
双指针方法的原理? 从左到右扫描时,遇到不匹配的 ) 就重置;从右到左扫描时,遇到不匹配的 ( 就重置。两遍扫描可以覆盖所有情况
如何输出具体的最长有效括号子串? 在计算 dpi 时记录 maxLen 对应的位置,然后从该位置向前截取 maxLen 长度的子串
进阶:如何 O(1) 空间? 使用双指针方法:分别从左到右和从右到左扫描,处理不匹配的括号情况

相关题目

题号 题目 难度 核心思路
32 最长有效括号 困难 动态规划/栈/双指针
20 有效的括号 简单
22 括号生成 中等 DFS/回溯
301 删除无效的括号 困难 BFS/DFS

总结

要点 内容
核心思想 动态规划:以每个 ) 结尾计算最长有效括号长度
状态定义 dp[i] = 以第 i 个字符结尾的最长有效括号长度
状态转移 情况一:"()", dpi = dpi-2 + 2
状态转移 情况二:"(...])", dpi = dpi-1 + dpi-dp\[i-1-2] + 2
边界处理 需要判断索引是否越界
结果 返回 maxLen

相关推荐
杨航 AI20 分钟前
** AI 面试算法 50 题清单**,不是单纯的 LeetCode 题号列表,而是按照“**考察概率 × 面试价值 × 你当前准备方向**”排序
算法·leetcode·面试
m0_720245017 小时前
1543.统计好三元组(简单)
开发语言·算法
To_OC7 小时前
LC 560 和为 K 的子数组:前缀和配哈希表,这对组合我是真的服了
javascript·算法·程序员
hans汉斯8 小时前
《软件工程与应用》期刊推荐&10月版面征稿中
图像处理·人工智能·深度学习·算法·音视频·软件工程
叠层归一研究院8 小时前
AGI 系统(八):符号范畴嵌入函子 — SymCat ↪ C_M107 严格化
c语言·开发语言·人工智能·算法·transformer·agi
其美杰布-富贵-李9 小时前
08 进阶评估指标与算法特定指标
人工智能·算法
.道阻且长.10 小时前
5.LeetCode算法习题讲解--双指针--有效三角形的个数
算法·leetcode·职场和发展
我是海飞10 小时前
杰理JL703N SSD1306 OLED(128x64) 点阵屏移植说明
单片机·算法·嵌入式·音频·杰理
wabs66611 小时前
关于图论【最短路径之Bellman_ford 算法(单源有限最短路)|卡码网96.城市间货物运输III的思考】
数据结构·算法·图论·卡码网·bellman_ford·单源有限最短路
lsylalalala13 小时前
常见的排序算法1
数据结构·算法·排序算法