题目链接
Leetcode.32 最长有效括号
题目描述
给你一个只包含 '('
和 ')'
的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
输入:s = “(()”
输出:2
解释:最长有效括号子串是 “()”
示例 2:
输入:s = “)()())”
输出:4
解释:最长有效括号子串是 “()()”
示例 3:
输入:s = “”
输出:0
提示:
- 0 < = s . l e n g t h < = 3 ∗ 1 0 4 0 <= s.length <= 3 * 10^4 0<=s.length<=3∗104
- s[i] 为
'('
或')'
解法一:动态规划
我们可以定义一个数组
f
[
n
]
f[n]
f[n] ,
f
[
i
]
f[i]
f[i] 代表的是 以 字符
s
[
i
]
s[i]
s[i] 结尾的合法括号子串的最大长度。
举例:
s
=
"
(
(
)
)
)
"
s = "(()))"
s="(()))" ,
f
=
[
0
,
0
,
2
,
4
,
0
]
f = [0,0,2,4,0]
f=[0,0,2,4,0]
所以,按照我们的定义,最终的答案就是 m a x ( f [ 0 ] , f [ 1 ] , f [ 2 ] , . . . . , f [ n − 1 ] ) max(f[0],f[1],f[2],....,f[n-1]) max(f[0],f[1],f[2],....,f[n−1])。
状态转移:
实际上我们只需要讨论
s
[
i
]
=
′
)
′
s[i] = ')'
s[i]=′)′ 的情况。
当 s [ i ] = ′ ) ′ s[i] = ')' s[i]=′)′ 时,有两种情况:
- 此时 s [ i − 1 ] = ′ ( ′ s[i-1] = '(' s[i−1]=′(′,如同这样的形式 s = " . . . . . . ( ) " s = "......()" s="......()",此时 f [ i ] = f [ i − 2 ] + 2 f[i] = f[i-2] + 2 f[i]=f[i−2]+2
- 此时 s [ i − 1 ] = ′ ) ′ s[i-1] = ')' s[i−1]=′)′,如同这样的形式 s = " . . . . . . ) ) " s = "......))" s="......))",并且如果此时 s [ i − f [ i − 1 ] − 1 ] = ′ ( ′ s[i - f[i - 1] - 1] = '(' s[i−f[i−1]−1]=′(′, f [ i ] = f [ i − 1 ] + f [ i − f [ i − 1 ] − 2 ] + 2 f[i] = f[i-1] + f[i-f[i-1]-2]+2 f[i]=f[i−1]+f[i−f[i−1]−2]+2
- 解释一下: 加入此时 s = “…(
()()
)” -
- s [ i ] s[i] s[i] 此时指的是最后一个字符 ‘)’, f [ i − 1 ] f[i-1] f[i−1] 指的是红色部分的长度4, s [ i − f [ i − 1 ] − 1 ] s[i-f[i-1]-1] s[i−f[i−1]−1] 指的是加粗的那个字符 ‘(’。
-
- 所以当 s [ i − f [ i − 1 ] − 1 ] = ′ ( ′ s[i-f[i-1]-1] = '(' s[i−f[i−1]−1]=′(′ 时,以 s [ i ] s[i] s[i] 结尾的最大合法括号子串的长度要加2
-
- f [ i − f [ i − 1 ] − 2 ] f[i-f[i-1]-2] f[i−f[i−1]−2] 代表 以加粗 ‘(’ 左边一个位置的字符为结尾的 最大合法括号子串的长度。因为我们要求的最大的连续长度,所以需要加上这个。
C++代码:
class Solution {
public:
int longestValidParentheses(string s) {
int n = s.size();
vector<int> f(n);
int ans = 0;
for(int i = 1;i < n;i++){
if(s[i] == ')'){
if(s[i-1] == '('){
f[i] = (i >= 2 ? f[i-2] : 0) + 2;
}
else if(i - f[i-1] - 1 >= 0 && s[i - f[i-1] - 1] == '('){
f[i] = f[i-1] + (i - f[i-1]-2 >= 0 ? f[i-f[i-1]-2] : 0) + 2;
}
}
ans = max(ans,f[i]);
}
return ans;
}
};
Java代码:
class Solution {
public int longestValidParentheses(String s) {
int ans = 0;
int[] f = new int[s.length()];
for (int i = 1; i < s.length(); i++) {
if (s.charAt(i) == ')') {
if (s.charAt(i - 1) == '(') {
f[i] = (i >= 2 ? f[i - 2] : 0) + 2;
} else if (i - f[i - 1] > 0 && s.charAt(i - f[i - 1] - 1) == '(') {
f[i] = f[i - 1] + ((i - f[i - 1]) >= 2 ? f[i - f[i - 1] - 2] : 0) + 2;
}
ans = Math.max(ans, f[i]);
}
}
return ans;
}
}
- 时间复杂度: O ( n ) O(n) O(n)
解法二:栈
一般对于这种括号的题,都可以先思考一下能否使用栈来解决。
我们要保持栈底始终为 不被匹配的右括号的下标
。
- 对于遇到的 ‘(’ ,直接将它的下标入栈
- 对于遇到的 ‘)’ ,先弹出栈顶元素表示匹配了当前的右括号:
-
- 如果现在栈为空,就说明当前的右括号是不被匹配的(
对于下一段合法的连续括号子串而言
),将当前右括号的下标入栈
- 如果现在栈为空,就说明当前的右括号是不被匹配的(
-
- 如果栈不为空,就用当前右括号的下标 - 栈顶元素,即为以当前右括号结尾的最大连续括号子串的长度 l e n len len,用这个长度 l e n len len 更新答案。
- 为了处理边界情况(
以左括号开头的s
),在开始循环之前,要在栈里插入 -1。
- 时间复杂度: O ( n ) O(n) O(n)
C++代码:
class Solution {
public:
int longestValidParentheses(string s) {
stack<int> stk;
stk.push(-1);
int n = s.size();
int ans = 0;
for(int i = 0;i < n;i++){
if(s[i] == '(') stk.push(i);
else{
stk.pop();
if(!stk.empty()) ans = max(ans,i - stk.top());
else stk.push(i);
}
}
return ans;
}
};
Java代码:
class Solution {
public int longestValidParentheses(String s) {
int ans = 0;
Stack<Integer> stack = new Stack<>();
stack.push(-1);
int n = s.length();
for(int i = 0;i < n;i++){
if(s.charAt(i) == '(') stack.push(i);
else{
stack.pop();
if(!stack.isEmpty()){
ans = Math.max(ans,i - stack.peek());
}
else stack.push(i);
}
}
return ans;
}
}