目录

1、题目

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

示例 1:

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

示例 2:

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

示例 3:

输入:s = ""
输出:0

提示:

  • ​0 <= s.length <= 3 * 10^4​
  • ​s[i] 为 '(' 或 ')'​

2、思路

(栈) LeetCode 32. 最长有效括号【c++/java详细题解】_栈

我们可以发现一个规律,每一段合法括号序列它在字符串​​s​​中出现的位置一定是连续且不相交的,如下图所示:


LeetCode 32. 最长有效括号【c++/java详细题解】_力扣高频面试题_02

因此我们能想到的最直接的做法是找到每个可能的子串后判断它是否为合法括号序列,但这样的时间复杂度会达到 LeetCode 32. 最长有效括号【c++/java详细题解】_力扣高频面试题_03

有没有一种更高效的做法?

我们知道栈在处理括号匹配有着天然的优势,于是考虑用栈去判断序列的合法性。遍历整个字符串​​s​​​,把所有的合法括号序列按照右括号来分类,对于每一个右括号,都去求一下以这个右括号为右端点的最长的合法括号序列的左端点在什么位置。我们把每个右括号都枚举一遍之后,再取一个​​max​​,就是整个的最大长度。

具体过程如下:

  • 1、用栈维护当前待匹配的左括号的位置,同时用​​start​​ 记录一个新的可能合法的子串的起始位置,初始设为​​0​​。
  • 2、如果​​s[i] =='('​​,那么把​​i​​进栈。
  • 3、如果​​s[i] == ')'​​,那么弹出栈顶元素 (代表栈顶的左括号匹配到了右括号),出栈后:
  • 如果栈为空,说明以当前右括号为右端点的合法括号序列的左端点为​​start​​,则更新答案 ​​i - start + 1​​。
  • 如果栈不为空,说明以当前右括号为右端点的合法括号序列的左端点为栈顶元素的下一个元素,则更新答案​​i - st.top()​​ 。LeetCode 32. 最长有效括号【c++/java详细题解】_子串_04
  • 4、遇到右括号​​)​​且当前栈为空,则当前的 ​​start​​开始的子串不再可能为合法子串了,下一个合法子串的起始位置是 ​​i + 1​​,更新 ​​start = i + 1​​。
  • 5、最后返回答案即可。

实现细节: 栈保存的是下标。

时间复杂度分析: 每个位置遍历一次,最多进栈一次,故时间复杂度为 LeetCode 32. 最长有效括号【c++/java详细题解】_栈

3、c++代码

class Solution {
public:
int longestValidParentheses(string s) {
stack<int> st;
int ans = 0;
for (int i = 0,start = 0; i < s.size(); i++)
{
if (s[i] == '(') st.push(i); //左括号入栈
else
{
if (!st.empty())
{
st.pop(); //匹配成功
if (st.empty()) ans = max(ans, i - start + 1);
else ans = max(ans, i - st.top()); //i - st.top() + 1 - 1
}
else start = i + 1; //更新起点
}
}
return ans;
}
};

4、java代码

class Solution {
public int longestValidParentheses(String s) {
Stack<Integer> st = new Stack<Integer>();
int ans = 0;
for(int i = 0 ,start = 0;i < s.length();i ++)
{
if( s.charAt(i) == '(') st.add(i);
else
{
if(!st.isEmpty())
{
st.pop();
if(st.isEmpty()) ans = Math.max(ans,i - start + 1);
else ans = Math.max(ans,i - st.peek());
}
else start = i + 1;
}
}
return ans;
}
}

原题链接:​32. 最长有效括号​

LeetCode 32. 最长有效括号【c++/java详细题解】_栈_06