力扣 32. 最长有效括号(困难)
栈
这个题目我最开始想到的是栈,遍历字符串的过程中,需要记录每一个合法子串的长度,这也是难点所在,因为在遍历过程中不方便记录每一个合法子串的长度,因此这里需要记录一个“最后一个没有被匹配的右括号的下标”,初始栈时为了记录“最后一个没有被匹配的右括号的下标”,会将-1
入栈。
遍历过程中一共有两种情况:
- 匹配到左括号‘(’,此时入栈即可。
- 匹配到右括号’)',此时先出栈,出栈后判断栈是否为空:
- 如果为空,表面该右括号是没有被匹配的,因为栈中必须要保证有一个“最后一个没有被匹配的右括号的下标”,因此该右括号的下标入栈。
- 如果不为空,则表明匹配成功,那么此时该右括号的位置减去栈顶元素的位置即可得到子串长度。
遍历过程中记录最大的长度则为最终结果。
代码
class Solution {
public int longestValidParentheses(String s) {
if (s.length() == 0) return 0;
int res = 0;
Stack<Integer> st = new Stack<>();
st.push(-1);
for (int i = start; i <= end; i++) {
if (s.charAt(i) == '(') {
st.push(i);
} else {
st.pop();
if (!st.isEmpty()) {
res = Math.max(res, i - st.peek());
} else {
st.push(i);
}
}
}
return res;
}
}
动态规划
按照卡尔哥的动规五部曲来做:
- 定义数组
int[] dp = new int[s.length() - 1];
表示从0到i的范围内最长的有效括号长度为dp[i]
。 - 定义状态转移方程。这里分情况讨论:
- 如果当前第
i
个元素为')'
,则看i - 1
元素:- 如果
i - 1
为'('
,如果i == 1
则dp[i]
直接赋值为2。 - 否则,
dp[i] = dp[i - 2] + 2
,因为此时这一对括号已经配对好了,那么就需要根据这对括号前一位的值来更新。
- 如果
- 如果当前第
i
个元素为'('
,则表明此时可能有嵌套的情况,例如()(())
这种,当i = 5
时,首先根据i - 1
获得内部括号的个数,即dp[i - 1
,再判断i - dp[i - 1] - 1
位置是否为'('
,如果是,则表明外层括号也匹配成功,dp[i] = dp[i - 1] + 2
,由于题目求的时最长有效括号,因此还要判断最外层的前一个元素是否也是匹配成功的,如果是则加上去,但是此时有可能会越界(例如(())()
,此时嵌套发生在下标为3的位置,i - dp[i] = -1
),因此要做个判断:if (i - dp[i] >= 0)
,成立则dp[i] += dp[i - dp[i]]
- 如果当前第
代码
class Solution {
public int longestValidParentheses(String s) {
int res = 0;
// 定义dp[i]表示第i个字符时最长有效括号字串长度为dp[i]
// 初始化,全部默认为0
int[] dp = new int[s.length()];
// 定义count对)计数
int count = 0;
// 遍历顺序,从下标为1的开始
for (int i = 1; i < s.length(); i++) {
if (s.charAt(i) == ')') {
if (s.charAt(i - 1) == '(') {
// 如果是第一对括号
if (i == 1) {
dp[i] = 2;
} else {
dp[i] = dp[i - 2] + 2;
}
} else {
// 计算出左括号的位置,判断是否越界
if (i - dp[i - 1] - 1 >= 0) {
// 判断是否为左括号
if (s.charAt(i - dp[i - 1] - 1) == '(') {
// 如果是,则直接加2
dp[i] = dp[i - 1] + 2;
// 如果与前面的连续,此时也要加上前面的值
if (i - dp[i] >= 0)
dp[i] += dp[i - dp[i]];
}
}
}
}
res = Math.max(res, dp[i]);
}
return res;
}
}
这是按照我个人的理解来写的,如果感觉不太明白可以去力扣看官方题解。