当前位置: 首页 > article >正文

[leetcode] 32. 最长有效括号

文章目录

  • 题目描述
  • 解题方法
    • 方法一:栈
      • java代码
      • 复杂度分析
    • 方法二:贪心
      • java代码
      • 复杂度分析
  • 相似题目

题目描述

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

示例 1:

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

示例 2:

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

示例 3:

输入:s = ""
输出:0

提示:

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

解题方法

方法一:栈

利用栈先进后出的特性,每次匹配到'('字符,将'('字符的下标位置入栈;当匹配到')' 字符时,将栈顶一个'('字符的下标位置出栈,出栈的'('字符就是对应')'字符匹配的左括号。设匹配到')' 字符下标位置为j,对应出栈'('字符下标位置为ij - i + 1即为当前的’)'字符匹配的括号长度。

通过上面这种方式我们能求出所有’)'对应'('的括号长度,取其中的最大值就是最长有效括号子串的长度吗?答案是否定的。还有一种情况,虽然匹配到了当前')'匹配到了'(',但是与匹配的'('相邻的前面字符串中也可能存在有效的括号子串。此时,就需要将前面子串的括号长度相加。

举个例子,设字符串为" ( ) ( ) \color {red}()\color {green}() ()()",此时绿色的括号匹配长度是2,但我们还需要加上前面红色括号的匹配长度,才能求出最长有效括号子串的长度。

友情提示:以下是正确思路

那我们需要怎么做呢?其实也很简单,只需要提供一个变量start,用来记录有效括号子串的起始位置。初始栈为空时,加入的第一个'('的下标即为有效括号子串的起始位置。而当栈中没有'(',而又匹配到')'时,原来的start位置废弃,后面加入的第一个'('的下标即为新的start。那么当匹配到')'时,如何计算最长有效括号子串的长度呢?分以下两种情况讨论。

  • 第一种,当匹配到的字符')'有对应出栈'('字符,且'('出栈后栈不为空。设当前')'下标为j'('出栈后栈顶元素为i,则当前')'匹配的有效括号子串的长度为j - i
  • 第二种,当匹配到的字符')'有对应出栈'('字符,且'('出栈后栈为空。设当前')'下标为j,则当前')'匹配的有效括号子串的长度为j - start + 1

取上面两种情况最大值即为最长有效括号子串的长度。

java代码

public int longestValidParentheses(String s) {
    if (s == null || s.length() == 0) {
        return 0;
    }
    // 记录有效子串的起始位置
    int start = 0;
    int max = 0;
    Stack<Integer> stack = new Stack<Integer>();
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (c == '(') {
            stack.push(i);
        } else if (c == ')') {
            if (stack.isEmpty()) {
                start = i + 1;
            } else {
                stack.pop();
                if (stack.isEmpty()) {
                    // 栈为空时,有效子串的长度 = 右括号的位置 - 有效子串的起始位置 + 1
                    max = Math.max(max, i - start + 1);
                } else {
                    // 栈不为空时,有效子串的长度 = 右括号的位置 - 栈顶元素的位置,因为栈顶元素的下一个位置是匹配的'('
                    max = Math.max(max, i - stack.peek());
                }
            }
        }

    }
    return max;
}

复杂度分析

时间复杂度: O ( N ) O(N) O(N) N N N为字符串长度。需要遍历一次字符串。
空间复杂度: O ( N ) O(N) O(N),需要提供栈的存储空间。

方法二:贪心

方法一需要用栈存储左括号位置,那么有没有一种方法只使用 O ( 1 ) O(1) O(1)的常数空间呢?答案是有的,不过需要遍历两次字符串,我说一下思路。

第一次遍历,我们需要从头到尾遍历字符串,我们记 l e f t left left为匹配的'('数量, r i g h t right right为匹配的')'数量数量。当 r i g h t > l e f t right>left right>left时, l e f t left left r i g h t right right置为 0 0 0;当 r i g h t = = l e f t right==left right==left时,计算当前字符串的有效长度为 2 × r i g h t 2 \times right 2×right,并且记录目前为止最长字符串长度。此次遍历,有一种情况没有考虑到。例如字符串" ( ( ) (() (()",当 l e f t left left一直大于 r i g h t right right时,是求不出最长长度的。

所以需要第二次遍历,这次我们从尾到头遍历字符串。还是记 l e f t left left为匹配的'('数量, r i g h t right right为匹配的')'数量数量。当 l e f t > r i g h t left>right left>right时, l e f t left left r i g h t right right置为 0 0 0;当 l e f t = = r i g h t left==right left==right时,计算当前字符串的有效长度为 2 × l e f t 2 \times left 2×left,并且记录目前为止最长字符串长度。

java代码

public int longestValidParentheses(String s) {
    int left = 0, right = 0;
    int max = 0;
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (c == '(') {
            left += 1;
        } else {
            right += 1;
        }
        if (right > left) {
            left = 0;
            right = 0;
        } else if (right == left) {
            max = Math.max(right * 2, max);
        }
    }
    left = right = 0;
    for (int i = s.length() - 1; i >= 0; i--) {
        char c = s.charAt(i);
        if (c == '(') {
            left += 1;
        } else {
            right += 1;
        }
        if (left > right) {
            left = 0;
            right = 0;
        } else if (left == right) {
            max = Math.max(left * 2, max);
        }
    }
    return max;
}

复杂度分析

时间复杂度: O ( N ) O(N) O(N) N N N为字符串长度。需要遍历两次字符串。
空间复杂度: O ( 1 ) O(1) O(1),只需要存储常数级别的变量。

相似题目

[leetcode] 20. 有效的括号


  • 个人公众号
    个人公众号
  • 个人小游戏
    个人小游戏

http://www.kler.cn/news/232950.html

相关文章:

  • 【Git】Windows下通过Docker安装GitLab
  • 计算机自顶向下 Wireshark labs——DNS
  • Arcgis使用过程中常见问题解决方法
  • ArcGIS学习(六)地理数据库
  • 恒创科技:怎么看云主机的性价比
  • rust语言tokio库底层原理解析
  • Linux查询指令
  • PneumoLLM:少样本大模型诊断尘肺病新方法
  • Android 13.0 系统framework修改低电量关机值为3%
  • Python HttpServer 之 简单快速搭建本地服务器,并且使用 requests 测试访问下载服务器文件
  • 医学图像隐私保护
  • OCP使用CLI创建和构建应用
  • vue3 之 商城项目—登陆
  • 政安晨:示例演绎TensorFlow的官方指南(三){快速使用数据可视化工具TensorBoard}
  • JSP编程
  • MySQL ——group by子句使用with rollup
  • npm 下载报错
  • 利用LLM大模型生成sql的深入应用探究
  • nvm安装node后,npm无效
  • “Hopf Oscillator-Based Gait Transition for A Quadruped Robot“代码复现
  • 春节:当代发展及创新传承
  • 揭开Markdown的秘籍:标题|文字样式|列表
  • c#cad 创建-圆(二)
  • 飞天使-k8s知识点13-kubernetes散装知识点2-statefulsetdaemonset
  • Red Panda Dev C++ Maker 使用说明
  • 攻防世界 CTF Web方向 引导模式-难度1 —— 1-10题 wp精讲
  • Git远程仓库的使用(Gitee)及相关指令
  • 【初中生讲机器学习】6. 分类算法中常用的模型评价指标有哪些?here!
  • 《游戏引擎架构》 -- 学习2
  • curl8.6.0 - CURLE_PEER_FAILED_VERIFICATION