牛客题解 | 最长的括号子串

news/2025/2/21 9:53:16/文章来源:https://www.cnblogs.com/wc529065/p/18728505

题目

题目链接

题目主要信息:
  • 一个长度为\(n\)的仅包含左右括号的字符串
  • 计算最长的格式正确的括号子串的长度
举一反三:

学习完本题的思路你可以解决如下题目:

BM65 最长公共子序列(二)

BM66.最长公共子串

BM71.最长上升子序列(一)

BM73 最长回文子串

BM75 编辑距离(一)

BM76 正则表达式匹配

方法一:栈(推荐使用)

知识点:栈

栈是一种仅支持在表尾进行插入和删除操作的线性表,这一端被称为栈顶,另一端被称为栈底。元素入栈指的是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;元素出栈指的是从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。

思路:

因为括号需要一一匹配,而且先来的左括号,只能匹配后面的右括号,因此可以考虑使用栈的先进后出功能,使括号匹配。

具体做法:

  • step 1:可以使用栈来记录左括号下标。
  • step 2:遍历字符串,左括号入栈,每次遇到右括号则弹出左括号的下标。
  • step 3:然后长度则更新为当前下标与栈顶下标的距离。
  • step 4:遇到不符合的括号,可能会使栈为空,因此需要使用start记录上一次结束的位置,这样用当前下标减去start即可获取长度,即得到子串。
  • step 5:循环中最后维护子串长度最大值。

图示:

alt

Java实现代码:

import java.util.*;
public class Solution {public int longestValidParentheses (String s) {int res = 0;//记录上一次连续括号结束的位置int start = -1; Stack<Integer> st = new Stack<Integer>();for(int i = 0; i < s.length(); i++){//左括号入栈if(s.charAt(i) == '(') st.push(i);//右括号else{ //如果右括号时栈为空,不合法,设置为结束位置if(st.isEmpty()) start = i;else{//弹出左括号st.pop(); //栈中还有左括号,说明右括号不够,减去栈顶位置就是长度if(!st.empty()) res = Math.max(res, i - st.peek());//栈中没有括号,说明左右括号行号,减去上一次结束的位置就是长度else res = Math.max(res, i - start);}}}return res;}
}

C++实现代码:

class Solution {
public:int longestValidParentheses(string s) {int res = 0;//记录上一次连续括号结束的位置int start = -1; stack<int> st;for(int i = 0; i < s.length(); i++){//左括号入栈if(s[i] == '(') st.push(i);//右括号else{ //如果右括号时栈为空,不合法,设置为结束位置if(st.empty()) start = i;else{//弹出左括号st.pop(); //栈中还有左括号,说明右括号不够,减去栈顶位置就是长度if(!st.empty()) res = max(res, i - st.top());//栈中没有括号,说明左右括号行号,减去上一次结束的位置就是长度else res = max(res, i - start);}}}return res;}
};

Python代码实现:

class Solution:def longestValidParentheses(self , s: str) -> int:res = 0#记录上一次连续括号结束的位置start = -1 st = []for i in range(len(s)):#左括号入栈if s[i] == '(': st.append(i)#右括号else: #如果右括号时栈为空,不合法,设置为结束位置if len(st) == 0: start = ielse:#弹出左括号st.pop()#栈中还有左括号,说明右括号不够,减去栈顶位置就是长度if len(st) != 0: res = max(res, i - st[-1])#栈中没有括号,说明左右括号行号,减去上一次结束的位置就是长度else: res = max(res, i - start)return res

复杂度分析:

  • 时间复杂度:\(O(n)\),其中\(n\)为字符串长度,遍历整个字符串
  • 空间复杂度:\(O(n)\),最坏全是左括号,栈的大小为\(n\)
方法二:动态规划(扩展思路)

知识点:动态规划

动态规划算法的基本思想是:将待求解的问题分解成若干个相互联系的子问题,先求解子问题,然后从这些子问题的解得到原问题的解;对于重复出现的子问题,只在第一次遇到的时候对它进行求解,并把答案保存起来,让以后再次遇到时直接引用答案,不必重新求解。动态规划算法将问题的解决方案视为一系列决策的结果

思路:

像这种子串长度的题,一般都涉及状态转移,可以用动态规划的方式。

具体做法:

  • step 1:用\(dp[i]\)表示以下标为i的字符为结束点的最长合法括号长度。

  • step 2:很明显知道左括号不能做结尾,因此但是左括号都是\(dp[i]=0\)

  • step 3:我们遍历字符串,因为第一位不管是左括号还是右括号dp数组都是0,因此跳过,后续只查看右括号的情况,右括号有两种情况:

    • 情况一:
      alt

    如图所示,左括号隔壁是右括号,那么合法括号需要增加2,可能是这一对括号之前的基础上加,也可能这一对就是起点,因此转移公式为:\(dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2\)

    • 情况二:
      alt

    如图所示,与该右括号匹配的左括号不在自己旁边,而是它前一个合法序列之前,因此通过下标减去它前一个的合法序列长度即可得到最前面匹配的左括号,因此转移公式为:\(dp[i] = (i - dp[i - 1] > 1 ? dp[i - dp[i - 1] - 2] : 0) + dp[i - 1] + 2\)

  • step 4:每次检查完维护最大值即可。

Java实现代码:

import java.util.*;
public class Solution {public int longestValidParentheses (String s) {int res = 0;//长度为0的串或者空串,返回0if(s.length() == 0 || s == null) return res;//dp[i]表示以下标为i的字符为结束点的最长合法括号长度int[] dp = new int[s.length()];  //第一位不管是左括号还是右括号都是0,因此不用管for(int i = 1; i < s.length(); i++){ //取到左括号记为0,有右括号才合法if(s.charAt(i) == ')'){ //如果该右括号前一位就是左括号if(s.charAt(i - 1) == '(') //计数+dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; 2//找到这一段连续合法括号序列前第一个左括号做匹配else if(i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') dp[i] = (i - dp[i - 1] > 1 ? dp[i - dp[i - 1] - 2] : 0) + dp[i - 1] + 2;}//维护最大值res = Math.max(res, dp[i]); }return res;}
}

C++实现代码:

class Solution {
public:int longestValidParentheses(string s) {int res = 0;//长度为0的串,返回0if(s.length() == 0) return res;//dp[i]表示以下标为i的字符为结束点的最长合法括号长度vector<int> dp(s.length(), 0); //第一位不管是左括号还是右括号都是0,因此不用管for(int i = 1; i < s.length(); i++){ //取到左括号记为0,有右括号才合法if(s[i] == ')'){ //如果该右括号前一位就是左括号if(s[i - 1] == '(') //计数+2dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2; //找到这一段连续合法括号序列前第一个左括号做匹配else if(i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '(') dp[i] = (i - dp[i - 1] > 1 ? dp[i - dp[i - 1] - 2] : 0) + dp[i - 1] + 2;}//维护最大值res = max(res, dp[i]); }return res;}
};

Python代码实现:

class Solution:def longestValidParentheses(self , s: str) -> int:res = 0#长度为0的串,返回0if len(s) == 0: return res#dp[i]表示以下标为i的字符为结束点的最长合法括号长度dp = [0 for i in range(len(s))] #第一位不管是左括号还是右括号都是0,因此不用管for i in range(1, len(s)): #取到左括号记为0,有右括号才合法if s[i] == ')': #如果该右括号前一位就是左括号if s[i - 1] == '(': #计数+2if i >= 2: dp[i] = dp[i - 2] + 2else:dp[i] = 2 #找到这一段连续合法括号序列前第一个左括号做匹配elif i - dp[i - 1] > 0 and s[i - dp[i - 1] - 1] == '(':if i - dp[i - 1] > 1:dp[i] = dp[i - dp[i - 1] - 2] + dp[i - 1] + 2else:dp[i] = dp[i - 1] + 2#维护最大值res = max(res, dp[i]) return res

复杂度分析:

  • 时间复杂度:\(O(n)\),其中\(n\)为字符串长度,遍历一次字符串
  • 空间复杂度:\(O(n)\),动态规划辅助数组的长度为\(n\)

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

牛客题解 | 最长公共前缀

牛客输入输出题单题解题目 题目链接 题目主要信息:给定一个字符串数组,其中有n个字符串,求所有字符串的最长公共前缀 公共前缀是指所有字符串都共有的前面部分的子串,从第一个字符开始举一反三: 学会了本题的思路,你将可以解决类似的字符串问题: BM83. 字符串变形 BM85. …

牛客题解 | 旋转数组

牛客输入输出题单题解题目 题目链接 题目主要信息:一个长度为\(n\)的数组,将数组整体循环右移\(m\)个位置(\(m\)可能大于\(n\)) 循环右移即最后\(m\)个元素放在数组最前面,前\(n-m\)个元素依次后移 不能使用额外的数组空间举一反三: 学习完本题的思路你可以解决如下题目:…

牛客题解 | 括号生成

牛客输入输出题单题解题目 题目链接 题目主要信息:求n对括号的全部合法组合,左右括号之间任意组合,只要合法就行 需要输出所有的结果举一反三: 学习完本题的思路你可以解决如下题目: BM55. 没有重复项数字的全排列 BM56. 有重复项数字的全排列 BM58. 字符串的排列 方法:递…

牛客题解 | 按之字形顺序打印二叉树

牛客输入输出题单题解题目 题目链接 题目的主要信息:给定一个二叉树,返回该二叉树的之字形层序遍 第一层从左向右,下一层从右向左,一直这样交替举一反三: 学习完本题的思路你可以解决如下题目: JZ32. 从上往下打印二叉树 JZ78. 把二叉树打印成多行 方法一:非递归层次遍历…

牛客题解 | 接雨水问题

牛客输入输出题单题解题目 题目链接 题目主要信息:给定一个整型数组,数组每个元素表示下图所示的每列灰色柱子高度,数值都是非负数 在雨水(图中蓝色部分)不超过边界的情况下,问最多能有多少蓝色的格子 数组以外的区域高度视为0举一反三: BM93. 盛水最多的容器 方法:双指…

牛客题解 | 合并两个有序的数组

牛客输入输出题单题解题目 题目链接 题目主要信息:A与B是两个升序的整型数组,长度分别为\(n\)和\(m\) 需要将数组B的元素合并到数组A中,保证依旧是升序 数组A已经开辟了\(m+n\)的空间,只是前半部分存储的数组A的内容举一反三: 学习完本题的思路你可以解决如下题目: BM4. 合…

牛客题解 | 合并二叉树

牛客输入输出题单题解题目 题目链接 题目的主要信息:合并(相加)二叉树位置相同的节点 缺少的节点用另一棵树来补,若都缺则返回NULL举一反三: 学习完本题的思路你可以解决如下题目: BM28. 二叉树的最大深度 BM29. 二叉树中和为某一值的路径(一) BM31. 对称的二叉树 BM33…

牛客题解 | 反转字符串

牛客输入输出题单题解题目 题目链接 题目的主要信息:输入一个只包含小写字母的字符串 输出该字符串反转后的字符串举一反三: 学习完本题的思路你可以解决如下题目: BM87. 合并两个有序数组 BM88. 判断是否为回文字符串 方法一:双指针交换(推荐使用) 知识点:双指针 双指针…

DeepSeek+AnythingLLM,搭建本地AI知识库,真的太香了!三分钟搞定智能助手,小白也能轻松上手!

1、🌟 痛点暴击:你的知识管理还在原始时代吗? 你是否每次查找文档翻遍文件夹,会议纪要总在关键时刻“失踪”? 别慌!今天揭秘一个“真香”组合——DeepSeek+AnythingLLM,轻松搭建本地知识库,AI秒变你的“第二大脑”! 2、🚀 为什么选DeepSeek+AnythingLLM?三大优势碾…

H3C-msr-36-10引导损坏重装引导升级

1、去官网下载准备升级用的软件ipe 2、在用户 PC(假设 IP 地址为 192.168.0.23)上运行 TFTP Server 程序(3CDaemon软件),以 及正确的文件保存目录,并把待升级文件保存在 TFTP Server 的工作目录下。3、在用户 PC 上运行终端仿真程序,启动路由器,键入<Ctrl+B>,进…

专业文件摆渡系统vs传统文件传输,谁才是数据交换的王者?

一、传统文件传输方式的优缺点 在内外网隔离的环境下,文件摆渡是不可避免的需求,如何安全高效地进行文件传输成为了企业和组织面临的重要挑战。传统的文件传输方式,如U盘拷贝、FTP、邮件、网盘、网闸等,虽然简单易用,但在安全性、效率、管理等方面存在诸多不足。而专业的文…

AI短剧?清华大学DeepSeek手册 第Ⅴ册《DeepSeek与AI幻觉》

随着DeepSeek功能的不断发掘与技术的结合,各行各业纷纷尝试利用AI创造新的突破。清华大学推出了多部作品,包括《DeepSeek入门到精通》、《DeepSeek如何赋能职场》、《普通人如何抓住DeepSeek红利》和《DeepSeek+智灵DeepResearch 让科研像聊天一样简单》,从入门、职场、生活…