P32 最长有效括号
题目链接:32. 最长有效括号.
题目描述
给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。
示例:
输入: “(()”
输出: 2
解释: 最长有效括号子串为 “()”
输入: “)()())”
输出: 4
解释: 最长有效括号子串为 “()()”
题解
方法一:动态规划
思路
- ①将原问题分解为子问题
子问题:“求以下标为 i i i 的字符结尾的最长有效括号子串的长度”。定义 d p [ i ] dp[i] dp[i] 为以下标为 i i i 的字符结尾的最长有效括号子串的长度,共有 n n n 个子问题(将给定一个只包含 ‘(’ 和 ‘)’ 的字符串记为 s s s, n n n 为字符串 s s s 的长度),将这 n n n 个子问题都解决了,那么其中的最大值就是原问题的解了,这就满足了问题具有最优子结构性质。
- ②确定状态
子问题只与一个变量有关:最长有效括号子串结尾字符的下标 i i i, i i i 就是状态,所以我们只用一个一维数组就可以存储各个状态的值。共有 n n n 个状态,它们构成状态空间。
- ③确定一些边界状态(初始状态)的值
不论 s [ 0 ] s[0] s[0] 是什么,因为只有一个括号,所以不可能组成有效括号, d p [ 0 ] = 0 dp[0]=0 dp[0]=0
若 s [ 0 ] = ‘ ( ’ s[0]=‘(’ s[0]=‘(’ 且 s [ 1 ] = ‘ ) ’ s[1]=‘)’ s[1]=‘)’,那么这两个字符可以组成有效括号,以下标为 1 1 1 的字符结尾的最长有效括号子串的长度为 2 2 2, d p [ 1 ] = 2 dp[1]=2 dp[1]=2;否则 d p [ 1 ] = 0 dp[1]=0 dp[1]=0 。
-
④确定状态转移方程
当我们在求 d p [ i ] ( i > 1 ) dp[i](i>1) dp[i](i>1) 时- 若 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 − 1 ] s[i-1] s[i−1] 与 s [ i ] s[i] s[i] 已经可以凑成一对有效括号了,那么我们可以考虑: d p [ i ] = dp[i]= dp[i]= 以下标为 i − 2 i-2 i−2 的字符结尾的最长有效括号子串的长度 + 2 +2 +2,所以有 d p [ i ] = d p [ i − 2 ] + 2 dp[i]=dp[i-2]+2 dp[i]=dp[i−2]+2
- 若
s
[
i
−
1
]
=
‘
)
’
s[i-1]=‘)’
s[i−1]=‘)’
我们知道以 s [ i − 1 ] s[i-1] s[i−1] 为结尾字符的最长有效括号子串的长度为 d p [ i − 1 ] dp[i-1] dp[i−1] ,我们将这个子串记为 s u b sub sub,字符 s [ i ] = ‘ ) ’ s[i]=‘)’ s[i]=‘)’ 要想加入 s u b sub sub,得到一个以字符 s [ i ] s[i] s[i] 为结尾字符的、包含 s u b sub sub 的一个更长的有效括号子串,那么 s u b sub sub 前的一个字符 s [ i − d p [ i − 1 ] − 1 ] s[i-dp[i-1]-1] s[i−dp[i−1]−1] 必须为 ‘ ( ’ ‘(’ ‘(’,才能和 字符 s [ i ] = ‘ ) ’ s[i]=‘)’ s[i]=‘)’ 匹配,若这两个字符匹配,那么还需加上以字符 s [ i − d p [ i − 1 ] − 2 ] s[i-dp[i-1]-2] s[i−dp[i−1]−2] 为结尾的最长有效括号子串的长度。即 d p [ i ] = d p [ i − 1 ] + d p [ i − d p [ i − 1 ] − 2 ] + 2 dp[i]=dp[i-1]+dp[i-dp[i-1]-2]+2 dp[i]=dp[i−1]+dp[i−dp[i−1]−2]+2
由以上的分析,我们可以得到状态转移方程:
d p [ i ] = { when s [ i ] = ‘ ( ’ 0 when s [ i ] = ‘ ) ’ { d p [ i − 2 ] + 2 if s [ i − 1 ] = ‘ ( ’ d p [ i − 1 ] + d p [ i − d p [ i − 1 ] − 2 ] + 2 if s [ i − 1 ] = ‘ ) ’ & s [ i − d p [ i − 1 ] − 1 ] = ‘ ( ’ dp[i] = \begin{cases} \text{when } s[i]=‘(’ \ \ \ \ \ 0 \\ \text{when } s[i]=‘)’ \begin{cases} dp[i-2]+2 &\text{if \ \ } s[i-1]=‘(’ \\ dp[i-1]+dp[i-dp[i-1]-2]+2 &\text{if \ \ } s[i-1]=‘)’ \ \ \& \ \ s[i-dp[i-1]-1]=‘(’ \end{cases} \end{cases} dp[i]=⎩⎪⎨⎪⎧when s[i]=‘(’ 0when s[i]=‘)’{dp[i−2]+2dp[i−1]+dp[i−dp[i−1]−2]+2if s[i−1]=‘(’if s[i−1]=‘)’ & s[i−dp[i−1]−1]=‘(’
-
原问题的解
最后,在给定的字符串
s
s
s (长度为
n
n
n)中,最长有效括号的长度
m
a
x
L
e
n
maxLen
maxLen 即数组
d
p
dp
dp 中的最大值。
m
a
x
L
e
n
=
max
(
d
p
[
i
]
)
,
(
0
⩽
i
⩽
n
−
1
,
i
∈
Z
)
maxLen=\max(dp[i]),(0\leqslant i\leqslant n-1,i\in \mathbb{Z})
maxLen=max(dp[i]),(0⩽i⩽n−1,i∈Z)
算法
class Solution {
public:
int longestValidParentheses(string s) {
if(!s.size()){
return 0;
}
int size=s.size();
vector<int> dp(size);
dp[0]=0;
if(s[0]=='('&&s[1]==')'){
dp[1]=2;
}
for(int i=2;i<size;++i){
dp[i]=0;
if(s[i]==')'){
if(s[i-1]=='('){
dp[i]=dp[i-2]+2;
}
else if((i-dp[i-1]>=1&&s[i-dp[i-1]-1]=='(')){
dp[i]=i-dp[i-1]>=2?(dp[i-1]+dp[i-dp[i-1]-2]+2):dp[i-1]+2;
}
}
}
int maxLen=0;
for(int i=0;i<size;++i){
maxLen=max(maxLen,dp[i]);
}
return maxLen;
}
};
时间复杂度
假设字符串 s s s 的长度为 n n n
- 时间复杂度:
O
(
n
)
O(n)
O(n),因为动态规划中状态的数目为
n
n
n,在计算状态
i
i
i 的值
d
p
[
i
]
dp[i]
dp[i] 时,计算时间是一个与
n
n
n 无关的常数,时间复杂度为
O
(
1
)
O(1)
O(1),由动态规划解题的时间复杂度计算公式:
时 间 复 杂 度 = 状 态 的 数 目 ⋅ 计 算 每 个 状 态 所 需 时 间 时间复杂度=状态的数目⋅计算每个状态所需时间 时间复杂度=状态的数目⋅计算每个状态所需时间所以得到时间复杂度 O ( n ) O(n) O(n) 。 - 空间复杂度: O ( n ) O(n) O(n),需要一个长度为 n n n 一维数组 d p dp dp 来存储各个状态的值。