题目描述
给你一个只包含 '('
和 ')'
的字符串,找出最长有效(格式正确且连续)括号子串的长度。
样例
输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
输入:s = ""
输出:0
限制
0 <= s.length <= 3 * 10^4
s[i]
为'('
或')'
。
算法1
(两次线性扫描、贪心) O(n)
- 假设当前从前到后统计合法括号子串,令
(
的权值为1
,)
的权值为-1
。首先记录start
为某个起点,则在i
向后移动的过程中,若当前[start,i]
区间和等于0
,该字符串是合法的,更新答案;若区间和大于0
,则说明目前缺少右括号,可以不修改start
;若区间和小于0
,则说明区间已经不合法了,需要修正start
为i+1
。初始时start
从0
开始即可。 - 可是对于
...((((合法)(((
这种情况,以上算法不能够准确捕捉到最长的合法子串,此时我们逆向考虑,将以上过程反向,从后向前统计,即可处理所有的情况。
时间复杂度
- 两次线性扫描,故时间复杂度为 O(n)。
空间复杂度
- 仅需要常数的额外空间。
C++ 代码
class Solution {
public:
int longestValidParentheses(string s) {
int n = s.length();
int start = 0, val = 0, ans = 0;
for (int i = 0; i < n; i++) {
if (s[i] == '(') val++;
else val--;
if (val < 0) {
val = 0;
start = i + 1;
}
else if (val == 0)
ans = max(ans, i - start + 1);
}
start = n - 1; val = 0;
for (int i = n - 1; i >= 0; i--) {
if (s[i] == ')') val++;
else val--;
if (val < 0) {
val = 0;
start = i - 1;
}
else if (val == 0)
ans = max(ans, start - i + 1);
}
return ans;
}
};
算法2
(动态规划) O(n)
- 设 f(i) 为以 i 为结尾的最长合法子串。
- 初始时,f(0)=0。
- 转移时,我们仅考虑当前字符是
)
的时候。如果上一个字符是(
,即...()
结尾的情况,则 f(i)=f(i−1)+2。 - 如果上一个字符是
)
,即...))
的情况,则我们通过上一个字符的动规结果,判断是否能匹配末尾的)
。判断s[i - f(i - 1) - 1]
是(
,即...((合法))
,则可以转移 f(i)=f(i−1)+2+f(i−f(i−1)−2)。 - 最终答案为动规数组中的最大值。
时间复杂度
- 状态数为 O(n),每次转移有两种情况,故时间复杂度为 O(n)。
空间复杂度
- 需要额外 O(n) 空间的记录状态。
C++ 代码
class Solution {
public:
int longestValidParentheses(string s) {
int n = s.length();
vector<int> f(n, 0);
for (int i = 1; i < n; i++)
if (s[i] == ')') {
if (s[i - 1] == '(') {
if (i >= 2) f[i] = f[i - 2] + 2;
else f[i] = 2;
} else {
if (i - f[i - 1] - 1 >= 0 && s[i - f[i - 1] - 1] == '(')
if (i - f[i - 1] - 2 >= 0)
f[i] = f[i - 1] + 2 + f[i - f[i - 1] - 2];
else
f[i] = f[i - 1] + 2;
}
}
int ans = 0;
for (int i = 0; i < n; i++)
ans = max(ans, f[i]);
return ans;
}
};
算法3
(栈) O(n)
- 用栈维护当前待匹配的左括号的位置。同时用
start
记录一个新的可能合法的子串的起始位置。初始设为0
。 - 遇到左括号,当前位置进栈。
- 遇到右括号,如果当前栈不空,则当前栈顶出栈。出栈后,如果栈为空,则更新答案
i - start + 1
;否则更新答案i - st.top()
。 - 遇到右括号且当前栈为空,则当前的
start
开始的子串不再可能为合法子串了,下一个合法子串的起始位置是i + 1
,更新start = i + 1
。
时间复杂度
- 每个位置遍历一次,最多进栈一次,故时间复杂度为 O(n)。
空间复杂度
- 需要额外 O(n) 空间的维护栈。
C++ 代码
class Solution {
public:
int longestValidParentheses(string s) {
int n = s.length();
stack<int> st;
int start = 0, ans = 0;
for (int i = 0; i < n; 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());
} else {
start = i + 1;
}
}
}
return ans;
}
};
太棒了。
感觉leetcode 前面几百题
是那种感觉搞搞能做出来,但是递进好的做法挺多的那种,很有余味。
现在leetcode周赛季赛啥的,都是些犄角旮旯里的DP 各种复杂建图啥的,不会做就是不会做,没一点趣味性
动态规划Java版
class Solution { public int longestValidParentheses(String s) { int n = s.length(); char[] c = s.toCharArray(); // f[i]表示以下标i结尾的最长合法子串的长度 int[] f = new int[n + 1]; for (int i = 1; i < n; i++) {// 因为括号最少得两位,所以从下标1开始枚举字符 if (c[i] == ')') {// 如果当前字符为')' if (c[i - 1] == '(') {// 如果上一位字符为'(',此时为合法情况 if (i >= 2) f[i] = f[i - 2] + 2; else f[i] = 2; } else {// 如果为不合法情况,即...))的情况根据上一个字符的结果判断是否能匹配本次的')' if (i - f[i - 1] - 1 >= 0 && c[i - f[i - 1] - 1] == '(') {// ...((合法)) if (i - f[i - 1] - 2 >= 0) f[i] = f[i - 1] + 2 + f[i - f[i - 1] - 2]; else f[i] = f[i - 1] + 2; } } } } // 最终答案为f[i]中的最大值 int ans = 0; for (int i = 0; i < n; i++) ans = Math.max(ans, f[i]); return ans; } }
喵!
栈Java版
class Solution { /** 用栈维护当前待匹配'('的位置, 用start记录一个新的可能合法的子串的起始位置 */ public int longestValidParentheses(String s) { char[] c = s.toCharArray(); int n = c.length; int[] stk = new int[n + 10]; int tt = 0; int ans = 0; int start = 0; for (int i = 0; i < n; i++) { // 遇到左括号,当前位置入栈 if (c[i] == '(') stk[++tt] = i; else {// 遇到右括号 if (tt != 0) {// 若栈不为空 tt--;// 出栈一个左括号 if(tt == 0) ans = Math.max(ans, i - start + 1); else ans = Math.max(ans, i - stk[tt]); } else start = i + 1;// 若栈为空,则当前start开始的子串不可能成为合法子串了,下一个合法子串的起始位置为i+1 } } return ans; } }
f(i)=f(i−1)+2,这里转移方程写错了
感谢
太棒了
楼主出品,必属精品
太棒啦
懂了 补充一下:
因为val的值可能到底也清不到0 还大于0
而在这其中路过很多个合法的子括号串可能并不能统计出来
而反向用)来遍历就可以杜绝这种情况了
贪心看了半天没看懂为啥要前后遍历两次
只遍历一次的话,试一下这个例子’(((((()()’
第3种: st.top()存的不是'(' 或')'吗,状态更新的时候,好像不能ans = max(ans, i - st.top());吧。
栈中只存左括号的 下标。
很无敌!!!
太妙了啊。。。
太妙了!
这个做法简直妙啊~(≧▽≦)/~