最长有效括号

最长有效括号

https://leetcode.cn/problems/longest-valid-parentheses/

暴力法

首先是暴力法思路:判断字符串每一个偶数长度的子串是否是有效匹配括号,然后记录最大长度。判断一个子串是否是有效匹配的方式是栈(左括号入栈,右括号出栈,如果为有效匹配,最后栈为空)。

动态规划法

dp[i]代表以s[i]为结尾的最长有效括号字符串长度,dp数组的初始值应该为0。

一个字符不能组成有效的括号对,所以为dp[0]为0,然后从s[1]开始往后遍历并同时更新dp数组。

只有右括号才能组成有效括号,注意看下图:

当前遍历到第i位,如果第i-1位是左括号,那么要考虑串连的情况;如果第i-1位是右括号,那么要考虑嵌套+串连的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {
public int longestValidParentheses(String s) {
int ans =0;
int[] dp = new int[s.length()];
for(int i=1; i<s.length(); ++i){
if(s.charAt(i)==')'){
if(s.charAt(i)=='('){
dp[i] = 2;
// for case ..)()
if(i-2>=0) dp[i]+=dp[i-2];
}else{
if(i-dp[i-1]>0 && s.charAt(i-dp[i-1]-1)=='('){
dp[i] = 2 + dp[i-1];
// for case ..)(...))
if(i-dp[i-1]-2>=0) dp[i]+=dp[i-dp[i-1]-2];
}
}
ans = Math.max(ans, dp[i]);
}
}
return ans;
}
}

这里用动态规划的难点是下标难以搞懂。dp数组的含义是长度,位置i的右括号对应的最长有效括号的左括号位置是i-dp[[i]+1。

栈+动态规划

这将达到N的立方复杂度,有没有其他方法呢?来分析一下可能的括号匹配例子:

  • ()()() 串行括号
  • ((())) 嵌套括号

将栈修改为存字符的下标,遇到 ‘)’ 则计算栈顶下标差,然后出栈(如果栈空就跳过)。

这个朴素的想法可以应对嵌套括号,无法应对串行括号。根本原因在于忽略了前面的括号。

因此我们增加一个dp数组,用来记录下标i结尾的有效括号长度。计算栈顶下标差的同时还要加上栈顶前一个字符的有效括号长度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Solution {
public int longestValidParentheses(String s) {
Stack<Integer> stk = new Stack<Integer>();
int ans = 0;
int[] dp = new int[s.length()]; // dp[i] means s[i] is ')' and its longest valid parentheses
for(int i = 0 ,start = 0;i < s.length();i++){
if( s.charAt(i) == '(')
stk.add(i);
else{
if(stk.isEmpty()) continue;
int idx = stk.pop();
if(idx-1>=0)
dp[i] = dp[idx-1] + i-idx+1;
else
dp[i] = i-idx+1;
ans = Math.max(ans, dp[i]);
}
}
return ans;
}
}

双指针贪心法

用栈来进行匹配其实就是两个操作:push和pop。我们可以用这两个变量表示对应操作的数量,对于一个有效匹配:

  • 在任意时刻,push的数量大于等于pop的数量
  • 最后时刻,push的数量等于pop的数量

那么我们就可以用一个left表示左括号的数量,right表示右括号的数量,从左向右遍历,遇到相应括号时增加对应数量,然后进行判断:

  • 如果left==right,记录最大数量
  • 如果left<right,将这两者清空

但这无法应对((()的例子,因为在我们看来左括号过多并不违法,但有效的括号匹配要求left==right。

换个方向思考,从右到左遍历一遍,此时右括号过多并不违法。结合两者的最大值,就能得到答案了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public int longestValidParentheses(String s) {
int left = 0, right=0, ans=0, ans2=0;
int N = s.length();
// 从左往右遍历一次
for(int i=0; i<N; ++i){
if(s.charAt(i) == '(') left++;
else right++;
if(left == right) ans = Math.max(ans, 2*right);
else if (right > left){
left=0;
right=0;
}
}
// 从右往左再遍历一次
left = right =0;
for(int i=0; i<N; ++i){
if(s.charAt(N-1-i) == '(') left++;
else right++;
if(right == left) ans2 = Math.max(ans2, 2*left);
else if( left > right){
left=0;
right=0;
}
}
return Math.max(ans, ans2);
}
}
作者

Desirer

发布于

2024-06-05

更新于

2024-06-09

许可协议