LeetCode 32. Longest Valid Parentheses
问题描述
Given a string containing just the characters '('
and ')'
, find the length of the longest valid (well-formed) parentheses substring.
示例
Example 1
Input: "(()"
Output: 2
Explanation: The longest valid parentheses substring is "()"
Example 2
Input: ")()())"
Output: 4
Explanation: The longest valid parentheses substring is "()()"
题解
题目的意思很简单,就是找到最长的子串满足:'('
和 ')'
配对。
看起来跟那些寻找最长子串、和最大子串的题目差不多,于是我们可以尝试着用 DP 去解决它,首先我们看看怎样的子串可以满足要求:
"()"
、"()()"
"(())"
、"(()())"
总的来说,分为三类:单独成对、嵌套成对以及两者混合的。
那我们可以开始思考怎么去解决这个问题了,从头扫描输入串,并用 count
数组来记录以输入串同一索引所指元素作为子串最后一个字符的匹配子串长度 s
,用 i
表示当前扫描索引:
- 当
s[i] == '('
就将其索引压入栈(记住是索引,因为我们只把'('
压入栈),同时count[i] = count[i - 1]
- 当
s[i] == ')'
:- 如果栈非空,则弹出栈顶元素,同时
count[i] = count[i - 1] + 2
- 否则,
count[i] = 0
- 如果栈非空,则弹出栈顶元素,同时
解释一下:
- 当
s[i] == '('
时,我们并不知道它后面能不能构成匹配的子串,不妨假设它可以,我们可以连着前面字符构成一个子串; - 当
s[i] == ')'
时,我们可以通过栈是否为空来判断其是否能构成匹配的子串,如果可以,理所当然在前面的子串基础上填加了一对匹配的括号 ;如果不可以,当然是将count[i]
置 0,因为它将串给隔开了。
当然做到这步还不足够的!因为考虑这样的一个输入串:
s: ()(()
count: 02224
correct: 02002
很容易想到哪里出了问题:当 s[i] == '('
时我们是假设了它可以构成匹配的子串,当在上面的例子中 s[2]
这个 ‘(’
是不能构成匹配子串的。我们已经做到这一步了,能不能补救一下呢?
答案是可以的!还记得前面我们的栈压入的是索引吗?当我们扫描完,还存留在栈中的它们是不能成功匹配的 ')'
的索引。前面的问题就是我们没有考虑 '('
不是成功匹配子串一员导致 count[i]
加多了前面那一段的长度,所以减掉就行了:
# stack: 2
s: count:
0 ( 0
1 ) 2
2 ( 2 - count[2] = 0
3 ( 2 - count[2] = 0
4 ) 4 - count[2] = 2
一个包含多个未成功匹配的 '('
例子:
# stack: 2,5,6,7
s: count:
0 ( 0
1 ) 2
2 ( 2 - count[2] = 0
3 ( 2 - count[2] = 0
4 ) 4 - count[2] = 2
5 ( 4 - count[5] = 0
6 ( 4 - count[6] = 0
7 ( 4 - count[7] = 0
Code
class Solution {
public:
int longestValidParentheses(string s)
{
if (s.size() == 0)
return 0;
vector<int> count(s.size(), 0);
stack<int> st;
for (int i = 0; i < s.size(); ++i)
{
if (s[i] == '(')
{
st.push(i);
count[i] = count[i - 1];
}
else
{
// 不匹配
if (st.empty())
count[i] = 0;
// 匹配
else
{
count[i] = count[i - 1] + 2;
st.pop();
}
}
}
// 对不匹配的'('进行纠错
int begin, end = count.size() - 1;
while (!st.empty())
{
begin = st.top();
st.pop();
for (int i = begin + 1; i <= end; ++i)
count[i] -= count[begin];
count[begin] = 0;
end = begin - 1;
}
// 遍历 count 数组找出最大值
int maxCount = count[0];
for (auto i : count)
if (maxCount < i)
maxCount = i;
return maxCount;
}
};
复杂度
算法的复杂度一眼就可以看出来啦,我们正向扫描了输入串,以及在纠错过程中至多扫描一次输入串,所以复杂度为 O(n)