题目:
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"()()"
描述:
给出一个只包含左右小括号的字符串,输出这个字符串中最长的匹配括号子串的长度
分析:
一上来就去想只一次遍历怎么解决问题,
刚开始的想法是借用不久前做过的一道题的思路(在这里),控制左右括号的个数,分别为 left_count 和 right_count,在每个位置处,括号匹配的个数为 left_count - right_count,但是这样的话,对于多段匹配成功但是各段之间不连续的情况是无法成功判断的,
然后继续想,如何才能解决刚才思路中的问题呢?
哦,对,可以使用双栈,一个是工作栈,用来判断是否匹配,一个是存储栈,存储当前连续的且匹配的括号串,工作栈顶部元素匹配成功时,如果工作栈为空,则把当前匹配的两个括号放到存储栈中,并更新目标值,否则就先清空存储栈再放入刚匹配成功的括号对,但是这种情况是没办法处理类似 嵌套类型的括号的,
然后重新开始思考,发现刚刚那个思路的问题使得我发现了问题的关键所在:当前没有和任何右括号匹配的左括号也可能在后面找到一个右括号跟他成功匹配了!,也就是说,任何一个左括号都不能随便放弃,即使某个时刻不能作为最长可匹配括号串的起点,但是后面的某个时刻却有这个可能性,也就是说需要存储...
这样一来,就可能有很多个起点了,额....一时间感觉措手不及,难道要用递归吗???
实在没有头绪了,看了一个题解(大神的博客),发现自己的思路果然还没打开,还是没有梳理清楚思路,
虽然起点有很多个,但是某个起点由于被匹配而失去意义之后,上一次的起点则再次生效,这正符合栈的特点!
算法的步骤:
设置一个起点栈start(其实存储的是每个不连续的可匹配的段起点)来存储能匹配串的起点,设置left为当前工作区域能匹配的最小的下标
1,若当前字符串为左括号,则将当前坐标坐标入栈(任何左括号都可能是最长匹配串的起点)
2,若当前字符串为右括号,
(1)若起点栈为空,则说明没有左括号与之匹配,而且最长的可匹配串肯定是不包含这个字符的,需要重设当前能匹配的的起点left为当前坐标 + 1,
(2)若起点栈不为空,则出栈一个起点(其实就是等同于出栈一个左括号),
1)若此时起点栈为空,则说明没有暂存的起点,即没有不连续的可匹配的小段,则直接利用left值来尝试更新目标值
2)若此时起点栈不为空,则说明目前有能匹配的小段的起点,用最近的能匹配的小段的起点(起点栈的栈顶元素)去尝试更新目标值。
代码:(时间复杂度 O (n))
class Solution {
public:
int longestValidParentheses(string s) {
int index = 0, left = 0, result = 0;
stack<int> start;//起点栈
while (index < s.size()) {
if (s[index] == '(') {//左括号直接入栈
start.push(index);
} else {//若为右括号
if (start.empty()) {//若起点栈为空
left = index + 1; //说明不匹配,重置当前工作区域的起点
} else {//若起点栈不为空
start.pop();//则抛弃配对的括号,且将上次设置的左括号的起点抛弃
if (start.empty()) {//若起点栈为空,尝试用当前工作区域的匹配个数去更新目标值
result = max(result, index - left + 1);
} else {//若起点栈非空,则尝试使用使用最近一次的起点去更新目标值
result = max(result, index - start.top());
}
}
}
++ index;
}
return result;
}
};