判断有效括号
在这题之前要先了解“如何判断有效括号”,即将有效括号的判断,转化为元素的出入栈,并进一步用一个整型值来表示。这里有个引理,即遍历有效括号的过程中,出栈次数不可能大于入栈次数,整型值不可能为-1。即假如(
代表1,)
代表-1,不可能出现())(
这样的情况为有效括号,其中s[0:3]
的对应值为-1,s[4]
为1。
官方题解理解
https://leetcode-cn.com/problems/longest-valid-parentheses/solution/zui-chang-you-xiao-gua-hao-by-leetcode-solution/
官方给出了简洁的题解,其中比较难理解的是第二个if块,i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '('
,这个意思相当于是说,s[i-dp[i-1]-1]到s[i]应当形成如同((...))
的样式,其中(..)
的长度可以是0。
class Solution {
public int longestValidParentheses(String s) {
int maxans = 0;
int[] dp = new int[s.length()];
for (int i = 1; i < s.length(); i++) {
if (s.charAt(i) == ')') {
if (s.charAt(i - 1) == '(') {
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
} else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
}
maxans = Math.max(maxans, dp[i]);
}
}
return maxans;
}
第二个if块分析
为什么一定要i - dp[i - 1] > 0
呢?首先,s[i-dp[i-1]-1]到s[i]可以组成的字符串有如下几种情况(|
为字符串首部):
- 中间部分长度为0
i - dp[i - 1] > 0
,比如|...)
,如果左侧添个(
,就能形成有效括号。i - dp[i - 1] <= 0
,比如|)
,只有一个有括号,不能形成有效括号。
- 中间部分长度不为0
i - dp[i - 1] > 0
,比如|...())
,如果合起来的部分左侧再添个(
,就能组成有效括号。i - dp[i - 1] <= 0
,比如|())
,合起来的部分不能组成有效括号。为什么?
有人会疑惑2.2,当有效括号与一个右括号放在一起时,右括号有没可能与一部分字符串组成有效括号?比如下图
这是不可能的,用反证法可以证明。加入)
与中间某一段组成了有效括号,那么设)
为1,则中间那一段必须为-1,因为有效括号的整型值必须为0。同理,左边一段的整型值一定为+1。
而中间一段是不可能为-1的,因为我们看到中间段与左边段组成的有效括号,当遍历这段括号的过程中,整型值已经变成过一次-1,即小于0,出栈次数大于入栈次数,是不可能的。
所以,反证法证明2.2成立。
第二个if块总结
所以,合并条件可知,只要 i - dp[i - 1] > 0
,并在左侧添一个(
号,就可以形成有效括号了。再之后加上更左边一段的有效括号长度,即可得到完整的长度了。