32. 最长有效括号

目录

一、问题描述

二、解题思路

三、代码

四、复杂度分析


问题描述

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

解题思路 1:使用栈

我们可以通过使用栈来跟踪括号的匹配情况。当遇到左括号时,我们将它的索引压入栈中;当遇到右括号时,我们从栈中弹出索引,计算当前的有效括号长度。

详细步骤:
  1. 初始化栈 :我们将一个 -1 压入栈,用于处理边界情况。当我们找到有效的括号匹配时,可以通过栈顶元素计算长度。
  2. 遍历字符串
    • 如果当前字符是左括号 '(',将它的索引压入栈。
    • 如果是右括号 ')'
      • 如果栈非空,弹出栈顶元素。这是因为我们找到了一对匹配的括号。
      • 然后,检查栈是否为空。如果栈非空,计算当前有效括号的长度(当前索引减去栈顶索引),并更新最大长度。如果栈为空,说明这是一个不匹配的右括号,将当前索引压入栈用于标记新的起点。
  3. 返回最大有效括号的长度

代码实现

java 复制代码
class Solution {
    public int longestValidParentheses(String s) {
        // 初始化栈并压入 -1,用于处理边界情况
        Stack<Integer> stack = new Stack<>();
        stack.push(-1); // 用于标记起始的边界
        
        int maxLength = 0; // 记录最大长度
        
        // 遍历字符串
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                // 遇到左括号,压入当前索引
                stack.push(i);
            } else {
                // 遇到右括号,弹出栈顶元素
                stack.pop();
                
                if (!stack.isEmpty()) {
                    // 如果栈非空,计算当前有效括号的长度
                    maxLength = Math.max(maxLength, i - stack.peek());
                } else {
                    // 如果栈为空,将当前索引压入栈
                    stack.push(i);
                }
            }
        }
        
        return maxLength;
    }
}
时间复杂度:
  • O(n),因为我们只需要遍历字符串一次,并且每个元素最多只会被压入和弹出栈一次。
空间复杂度:
  • O(n),在最坏的情况下,栈的大小可能会达到字符串长度的一半。

解题思路 2:动态规划

我们还可以使用动态规划来解决这个问题。我们定义一个 dp 数组,其中 dp[i] 表示以索引 i 结尾的最长有效括号子串的长度。

详细步骤:
  1. 初始化 dp 数组 :长度为字符串 s 的长度,每个元素初始化为 0。
  2. 遍历字符串
    • 当遇到右括号 ')' 时,检查它前面一个字符:
      • 如果前一个字符是左括号 '(',那么当前括号可以与之匹配,长度为 dp[i - 2] + 2(即加上之前匹配的部分)。
      • 如果前一个字符是右括号 ')',那么需要检查是否可以通过之前的括号进行匹配。如果 s[i - dp[i - 1] - 1] == '(',说明我们可以匹配到左括号并更新 dp 值。
  3. 返回 dp 数组中的最大值
代码
java 复制代码
class Solution {
    public int longestValidParentheses(String s) {
        int n = s.length();
        // 初始化 dp 数组,所有值为 0
        int[] dp = new int[n];
        int maxLength = 0; // 记录最大长度
        
        // 遍历字符串,从第一个右括号开始
        for (int i = 1; i < n; i++) {
            if (s.charAt(i) == ')') {
                // 如果前一个字符是 '(',则可以匹配
                if (s.charAt(i - 1) == '(') {
                    dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
                }
                // 如果前一个字符是 ')',需要检查是否可以通过之前的匹配
                else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
                    dp[i] = dp[i - 1] + (i - dp[i - 1] >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
                }
                // 更新最大长度
                maxLength = Math.max(maxLength, dp[i]);
            }
        }
        
        return maxLength;
    }
}
时间复杂度:
  • O(n),因为我们只遍历一次字符串并且每个索引处进行常数时间的操作。
空间复杂度:
  • O(n) ,用于存储 dp 数组。

解题思路 3:双指针法(线性扫描)

我们还可以使用两个计数器 leftright 来分别计算当前扫描到的左括号和右括号的数量。通过双向扫描,我们可以有效找到最长有效括号。

详细步骤:
  1. 从左到右扫描 :遍历字符串,用 left 计数左括号,用 right 计数右括号。如果 right 超过 left,则意味着括号不匹配,重置计数器。如果 left == right,更新最大长度。
  2. 从右到左扫描 :类似地,从右向左扫描,用 leftright 进行计数。
代码
java 复制代码
class Solution {
    public int longestValidParentheses(String s) {
        int left = 0, right = 0, maxLength = 0;
        
        // 从左到右扫描
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                left++;
            } else {
                right++;
            }
            if (left == right) {
                maxLength = Math.max(maxLength, 2 * right);
            } else if (right > left) {
                left = right = 0;
            }
        }
        
        // 从右到左扫描
        left = right = 0;
        for (int i = s.length() - 1; i >= 0; i--) {
            if (s.charAt(i) == '(') {
                left++;
            } else {
                right++;
            }
            if (left == right) {
                maxLength = Math.max(maxLength, 2 * left);
            } else if (left > right) {
                left = right = 0;
            }
        }
        
        return maxLength;
    }
}
时间复杂度:
  • O(n),我们只需要两次线性扫描字符串。
空间复杂度:
  • O(1),只需要常数的额外空间。

总结:

  • 栈方法:使用栈来保存括号索引,时间复杂度 O(n),空间复杂度 O(n)。
  • 动态规划:通过动态规划记录每个位置的最长有效括号子串长度,时间复杂度 O(n),空间复杂度 O(n)。
  • 双指针法:通过两个计数器进行左右两次扫描,时间复杂度 O(n),空间复杂度 O(1)。
相关推荐
计算机毕设定制辅导-无忧学长2 分钟前
基于Spring Boot的玄幻小说个性化推荐平台设计与实现
java·vue.js·spring boot·后端·mysql·推荐算法
码路漫漫3 分钟前
LRU 真的能解决日志按业务 Key 分文件的落地瓶颈吗?
java·后端
禹凕8 分钟前
快速幂算法详解与实战
python·算法
xxwl58512 分钟前
高斯消元法异或版
人工智能·算法·机器学习
sylviiiiiia17 分钟前
leetcode hot 100
python·算法·leetcode
带刺的坐椅24 分钟前
不只是 Token 流:Solon AI 4.1 的语义化聊天事件
java·stream·event·solon-ai
延凡科技29 分钟前
建筑节能新视角:智慧冷站的数字化实践
java·人工智能·能源·暖通
励志不掉头发的内向程序员33 分钟前
【LibreCAD 2D架构】从鼠标点击到图形创建:RS_ActionDrawLine交互流程与状态机解析
开发语言·c++·qt·学习·系统架构·计算机外设·交互
www_aiyuanma_vip1 小时前
Java个人博客系统
java·开发语言
OPEN-F1 小时前
ROS2系列教程:Gazebo插件(关节控制/IMU/激光雷达)
c++·python·数码相机·算法·机器人