[Java·算法·困难]LeetCode32. 最长有效括号

news2025/8/7 15:17:07

每天一题,防止痴呆

  • 题目
  • 示例
  • 分析思路1
  • 题解1
  • 分析思路2
  • 题解2
  • 分析思路3
  • 题解3

👉️ 力扣原文

题目

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

示例

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

分析思路1

用栈的解法:

  1. 定义一个栈,用于存储字符在字符串中的下标。初始化时,将-1压入栈中。
  2. 从字符串的左边开始遍历,如果遇到左括号,则将其下标压入栈中。
  3. 如果遇到右括号,则弹出栈顶元素,表示与该右括号匹配的左括号已经找到。如果此时栈为空,则将该右括号的下标压入栈中,表示该右括号没有匹配的左括号。
  4. 如果栈不为空,则计算当前子串的长度,即当前右括号的下标减去栈顶元素的值。如果该子串的长度大于最长子串的长度,则更新最长子串的长度。

题解1

class Solution {
    public int longestValidParentheses(String s) {
        int maxLen = 0;
        Stack<Integer> stack = new Stack<>();
        stack.push(-1);//有效括号长度为当前右括号下标减去栈底元素,初始化要是-1才对
        for (int i=0; i<s.length(); i++){
            char c = s.charAt(i);
            if(c == '('){
                stack.push(i);
            }else {
                stack.pop();//弹出栈顶
                if(stack.isEmpty()){ // 栈为空,说明当前右括号没有与之匹配的左括号
                    stack.push(i);// 将当前右括号下标压入栈中
                }else {// 栈不为空,计算有效括号长度
                    int len = i - stack.peek();
                    maxLen = Math.max(maxLen, len);
                }
            }
        }
        return maxLen;
    }

}

执行结果
在这里插入图片描述

分析思路2

双指针:
在此方法中,我们利用两个计数器 left 和 right 。首先,我们从左到右遍历字符串,对于遇到的每个 ‘(’,我们增加 left 计数器,对于遇到的每个 ‘)’ ,我们增加 right 计数器。每当 left 计数器与 right 计数器相等时,我们计算当前有效字符串的长度,并且记录目前为止找到的最长子字符串。当 right 计数器比 left 计数器大时,我们将 left 和 right 计数器同时变回 0。

这样的做法贪心地考虑了以当前字符下标结尾的有效括号长度,每次当右括号数量多于左括号数量的时候之前的字符我们都扔掉不再考虑,重新从下一个字符开始计算,但这样会漏掉一种情况,就是遍历的时候左括号的数量始终大于右括号的数量,即 (() ,这种时候最长有效括号是求不出来的。

解决的方法也很简单,我们只需要从右往左遍历用类似的方法计算即可,只是这个时候判断条件反了过来:

当left 计数器比right 计数器大时,我们将left 和 right 计数器同时变回 0
当 left 计数器与 right 计数器相等时,我们计算当前有效字符串的长度,并且记录目前为止找到的最长子字符串
这样我们就能涵盖所有情况从而求解出答案。

题解2

class Solution {
    public int longestValidParentheses(String s) {
        int left=0,right=0,maxLen=0;
        for (int i = 0; i < s.length(); i++) {
            if(s.charAt(i)=='('){
                left++;
            }else {
                right++;
            }
            if(left == right){
                maxLen = Math.max(maxLen, right*2);
            }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){
                maxLen = Math.max(maxLen, right*2);
            }else if(left > right){
                left = right = 0;
            }
        }
        return maxLen;
    }

}

执行结果
在这里插入图片描述

分析思路3

使用动态规划:
我们定义一个一维数组 dp,其中 dp[i] 表示以 i 位置结尾的有效括号子串的最长长度。在计算 dp[i] 时,我们需要根据 s[i] 的值进行分类讨论:

1.当 s[i] 为左括号 ‘(’ 时,以 i 位置结尾的子串肯定不是有效括号子串,因此 dp[i] = 0。
2.当 s[i] 为右括号 ‘)’ 时,我们需要找到与其匹配的左括号。具体来说,我们可以先计算前面已经形成的有效括号长度 preLen,然后在 i - preLen - 1 的位置寻找与当前的右括号相匹配的左括号,即 s[pre]。如果 s[pre] 为左括号,则当前的右括号可以与其闭合,有效长度增加2。此时,我们还需要再往前看一眼,是否还有有效长度,如果有,合并过来。例如:“()(()())” 当前在计算最后一个位置时,dp[7] 已经等于 dp[6]+2 = 4+2,但需要再往前看一眼,dp[1] 还有有效长度,合并过来 dp[7] = 4+2+2。那是否还需要再往前看?不需要了,因为,如果前面还有有效长度,其长度肯定已经合并到 dp[2] 上了。因此,每次只需要再往前多看一眼就可以。

最终答案是 dp 数组中的最大值。

题解3

class Solution {
    // 子串问题:严格以每个结尾计算个答案,最终答案必在其中
    public static int longestValidParentheses(String s) {
        if (s == null || s.length() < 2) return 0;

        int[] dp = new int[s.length()]; // dp[i]:严格以i位置结尾,形成的有效括号子串最长长度是多少
        int max = 0; // 最终的答案

        // dp[0] = 0; // 默认

        for (int i = 1; i < s.length(); i++) {
            // if (s.charAt(i) == '(') dp[i] = 0; 以左括号结尾,无效
            
            if (s.charAt(i) == ')') {
                int preLen = dp[i - 1]; // 前面已经形成的有效括号长度
                int pre = i - 1 - preLen; // 寻找与当前的右括号相匹配的左括号位置:前面有效括号长度再往前一个位置

                if (pre >= 0 && s.charAt(pre) == '(') { // 如果寻找到左括号:前面有效括号长度再往前一个位置是左括号
                    dp[i] = dp[i-1] + 2; // 可以与当前的右括号闭合,有效长度增加2

                    // 【注意】此时,需要再往前看下,是否还有有效长度,如果有,合并过来
                    // 例如:"()(()())" 当前在计算最后一个位置时,dp[7]已经等于 dp[6]+2 = 4+2
                    // 但需要再往前看一眼,dp[1]还有有效长度,合并过来 dp[7] = 4+2+2
                    // 那是否还需要再往前看?
                    // 不需要了,因为,如果前面还有有效长度,其长度肯定已经合并到dp[2]上了
                    // 因此,每次只需要再往前多看一眼就可以
                    if (pre-1 >= 0) {
                        dp[i] += dp[pre-1];
                    }
                }

                max = Math.max(max, dp[i]); // 严格以每个结尾抓一个答案,最终答案必在其中
            }
        }

        return max;
    }

}

执行结果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/394025.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

开源鸿蒙南向嵌入学习笔记——NAPI框架学习(一)

开源鸿蒙南向嵌入学习笔记——NAPI框架学习&#xff08;一&#xff09; 前言——系列介绍 本系列文章主要是记录笔者在鸿蒙南向的学习与工作中的知识点笔记记录&#xff0c;其中不止会针对鸿蒙中的学习问题进行思考与记录&#xff0c;也会对涉及到的一些嵌入式等其他领域知识&…

[N0wayBack 练习题] My_enc,Euler,EasyLock,RRRRSA,EasyNumber,pwn

加入一个队,队里的练习题不少,还有WP真好My_enc原题from secret import flag import randomdef Cyber_key(LEN):Key [[] for i in range(row)]for x in range(row):for i in range(LEN):Key[x].append(random.randint(0, 2023))return Keydef Punk_enc(Key, msg):out []for l…

什么是API接口

API接口是指应用程序接口&#xff0c;是一种让不同的应用程序之间进行数据交互的方式。在现代软件开发中&#xff0c;API接口已经成为了必不可少的一部分。它们让开发者们可以将不同的功能组合在一起&#xff0c;同时也让不同的应用程序之间可以相互连接和通讯。API接口的作用A…

钓鱼客服到拿下服务器全过程(重点在于钓鱼添加img src)

重点总结 钓鱼时主动在变量中添加了字段&#xff0c;等待用户点击获取ip信息进行下一步资金盘plus呢 左看右看没啥东西&#xff0c;看看客服系统能不能打xss。 吊毛客服居然不在线&#xff0c;这套客服系统见过是whisper&#xff0c;之前审计过没有存储xss 但能通过伪造图片…

Codeforces Round 856 (Div. 2)(A~D)

A. Prefix and Suffix Array给出一个字符串的所有前缀和后缀&#xff0c;判断这个字符串是否是回文串。给出的前后缀都不是整个字符串。思路&#xff1a;如果是回文的话&#xff0c;那每一个等长的字符串也都是相同的。AC Code&#xff1a;#include <bits/stdc.h>typedef…

AQS底层源码深度剖析-BlockingQueue

目录 AQS底层源码深度剖析-BlockingQueue BlockingQueue定义 队列类型 队列数据结构 ArrayBlockingQueue LinkedBlockingQueue DelayQueue BlockingQueue API 添加元素 检索(取出)元素 BlockingQueue应用队列总览图 AQS底层源码深度剖析-BlockingQueue【重点中的重…

Spark Join大大表

Spark Join大大表分而治之拆分内表外表的重复扫描案例负隅顽抗数据分布均匀数据倾斜Task 数据倾斜Executor 数据倾斜两阶段 ShuffleExecutors 调优案例Join 大大表 : Join 的两张体量较大的事实表&#xff0c;尺寸相差在 3 倍内&#xff0c;且无法广播变量用大表 Join 大表才能…

观点丨Fortinet谈ChatGPT火爆引发的网络安全行业剧变

FortiGuard报告安全趋势明确指出“网络攻击者已经开始尝试AI手段”&#xff0c;ChatGPT的火爆之际的猜测、探索和事实正在成为这一论断的佐证。攻守之道在AI元素的加持下也在悄然发生剧变。Fortinet认为在攻击者利用ChatGPT等AI手段进行攻击的无数可能性的本质&#xff0c;其实…

动环监控4大应用价值,这个价值最大

机房管理&#xff0c;已成为企业管理和发展的首要任务。为提升机房管理的效率&#xff0c;应从空间环境、设备性能等多角度&#xff0c;对机房环境监测进行实时监控掌握相关数据参数&#xff0c;确保故障隐患及时发现&#xff0c;提高机房整体管理水平&#xff0c;降低运维难度…

Hadoop三大框架之HDFS

一、概述HDFS产生的背景及定义HDFS产生背景随着数据量越来越大&#xff0c;在一个操作系统存不下所有的数据&#xff0c;那么就分配到更多的操作系统管理的磁盘中&#xff0c;但是不方便管理和维护&#xff0c;需要一种系统来管理多台机器上的文件&#xff0c;这就是分布式文件…

一篇文章带你了解折线图

在我们的日常生活中&#xff0c;折线图随处可见&#xff0c;如医院里的心电图、股市里的股票走势、企业中的财务报表分析&#xff0c;但是折线图的具体适用情形&#xff0c;你真的了解吗&#xff1f; 折线图&#xff08;line chart&#xff09;也叫曲线图&#xff0c;用于显示数…

javaWeb在线考试系统

一、项目简介 本项目是一套javaWeb在线考试系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse 确保…

python-pandapower电力系统状态估计(算例1:讲解以及基本算例实现)

提示:专栏解锁后可以查看该专栏所有文章划算 全文截图如下 文章目录 前言一、状态估计1 理论背景2 测量量3 标准偏差二、工具箱函数讲解1 定义测量create_measurement()2 运行状态估计estimate( )3 不良数据处理remove_bad_data();chi2_analysis()remove_bad_data()…

k8s调度之初探nodeSelector和nodeAffinity

在k8s的调度中&#xff0c;有强制性的nodeSelector&#xff0c;节点亲和性nodeAffinity、Pod亲和性podAffinity、pod反亲和性podAntiAffinity。本篇先对nodeSelector和nodeAffinity做个初探。进入主题之前&#xff0c;先看看创建pod的大概过程kubectl向apiserver发起创建pod请求…

【UE4 RTS游戏】01-项目准备

步骤新建一个工程&#xff0c;选择俯视角游戏模板我命名工程如下&#xff1a;删除场景内的所有cube再删除Floor和Wall删除TopDownCharacter删除“NavgationMeshBoundVolume”删除“TamplateLabel”和“RecastNavMesh-Default”删除LightmassImportanceVolume、PostProcessVolum…

【java】java异常分类和异常处理以及面试中常见的问题

文章目录什么是异常&#xff1f;程序错误一般分为三种编译错误运行时错误逻辑错误两个子类区别java几种常见的异常&#xff1a;运行时异常&#xff1a;IOException异常的产生&#xff1a;异常的处理&#xff1a;消极的处理&#xff1a;积极的处理&#xff1a;(异常捕获)throw和…

Jenkins自动化部署入门

Jenkins自动化部署入门 一、简介 Jenkins是一个开源软件项目&#xff0c;是基于Java开发的一种持续集成工具&#xff0c;用于监控持续重复的工作&#xff0c;旨在提供一个开放易用的软件平台&#xff0c;使软件的持续集成变成可能。 Jenkins自动化部署实现原理 二、Jenkins部…

交互:可以执行命令行的框架才是好框架

上一节课&#xff0c;我们开始把框架向工业级迭代&#xff0c;重新规划了目录&#xff0c;这一节课将对框架做更大的改动&#xff0c;让框架支持命令行工具。 第三方命令行工具库 cobra obra 不仅仅能让我们快速构建一个命令行&#xff0c;它更大的优势是能更快地组织起有许多…

SpringBoot整合Mybatis详解

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、创建项目&#xff0c;导入依赖&#xff0c;完善项目结构二、编码1.yml配置2.编写实体类3.编写mapper.xml和接口4.编写业务层5.编写控制层6.启动类加上包扫描…

实践分享:Vue 项目如何迁移小程序

最近我们小组刚经历了将成熟的 HTML5 项目转换成小程序&#xff0c;并在app中运行的操作&#xff01;记录下来分享给各位。 项目&#xff1a;将已有的 Vue 项目转为小程序&#xff0c; 在集成了FinClip SDK 的 App 中运行。 技术&#xff1a;uni-app、FinClip 两个注意事项&…