32. 最长有效括号
dp数组含义
dp数组中
d
p
[
i
]
dp[i]
dp[i] 表示以下标
i
i
i 结尾的子串的最长有效括号的长度。如 ()()()
的 dp 数组为
[
0
,
2
,
0
,
4
,
0
,
6
]
[0,2,0,4,0,6]
[0,2,0,4,0,6] ; ()(())
的 dp 数组为
[
0
,
2
,
0
,
0
,
2
,
6
]
[0,2,0,0,2,6]
[0,2,0,0,2,6] 。
状态转移公式
如果
s
[
i
]
s[i]
s[i] 是 (
,那肯定不可能组成有效数组,
d
p
[
i
]
=
0
dp[i]=0
dp[i]=0 。
如果
s
[
i
]
s[i]
s[i] 是 )
,就有机会了,我们再分情况来看,按照其前一个字符来分:
- 如果
s
[
i
−
1
]
s[i-1]
s[i−1] 是
(
,这种情况比较简单, s [ i − 1 ] s[i-1] s[i−1] 和 s [ i ] s[i] s[i] 组成了一个有效的独立的括号串,相当于将 d p [ i − 2 ] dp[i-2] dp[i−2] 扩充了 2,即 d p [ i ] = d p [ i − 2 ] + 2 dp[i]=dp[i-2]+2 dp[i]=dp[i−2]+2 。
这里要注意是 i ≥ 2 i\ge 2 i≥2 的情况,如果 i < 2 i<2 i<2 (其实就只有 i = 1 i=1 i=1),则 d p [ i ] = 2 dp[i]=2 dp[i]=2 。
-
如果 s [ i − 1 ] s[i-1] s[i−1] 是
)
,就要复杂一点,这时最近的两个括号并不能组成有效括号串,想要整体组成有效的括号串,只有可能是分别跟更前面的两个(
组成。即类似:((xxx))
。详情看图。这种情况我们要先确认与 i i i 处的
)
对应的位置 i − d p [ i − 1 ] − 1 i-dp[i-1]-1 i−dp[i−1]−1 处是(
,否则肯定不匹配了,即确认 s [ i − d p [ i − 1 ] − 1 ] = ′ ( ′ s[i-dp[i-1]-1]='(' s[i−dp[i−1]−1]=′(′ (当然首先要确认 i − d p [ i − 1 ] > 0 i-dp[i-1]>0 i−dp[i−1]>0)。这时我们应该怎样更新此时的 d p [ i ] dp[i] dp[i] 呢,从图中可以看到, d p [ i ] dp[i] dp[i] 的值可能来自三部分:字符串最前面(可能存在)的一个独立的有效括号串,中间的有效括号串,和新增的一对括号即加2。因此 d p [ i ] = d p [ i − d p [ i − 1 ] − 2 ] + d p [ i − 1 ] + 2 dp[i]=dp[i-dp[i-1]-2]+dp[i-1]+2 dp[i]=dp[i−dp[i−1]−2]+dp[i−1]+2 。注意当 i − d p [ i − 1 ] < 2 i-dp[i-1]<2 i−dp[i−1]<2 时,最前面一部分不可能存在有效括号串,即第一项为零 d p [ i ] = d p [ i − 1 ] + 2 dp[i]=dp[i-1]+2 dp[i]=dp[i−1]+2 。
dp数组初始化
全部初始化为 0 即可。
遍历顺序
后面的 dp 数组的取值依赖于前面的 dp 数组的值,因此从前向后遍历即可。注意 d p [ 0 ] dp[0] dp[0] 仅有一个字符,不可能组成有效括号,因此从下标 1 开始遍历即可。
代码
class Solution {
public:
int longestValidParentheses(string s) {
int n = s.size();
vector<int> dp(n, 0);
int ans = 0;
for (int i=1; i<n; ++i) {
if (s[i] == '(') continue;
if (s[i-1] == '(') {
if (i >= 2) dp[i] = dp[i-2] + 2;
else dp[i] = 2;
}
else {
if (i-dp[i-1]>0 && s[i-dp[i-1]-1]=='(') {
if (i-dp[i-1] >= 2) dp[i] = dp[i-1] + dp[i-dp[i-1]-2] + 2;
else dp[i] = dp[i-1] + 2;
}
}
ans = max(ans, dp[i]);
}
return ans;
}
};