题目
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 "()()"
题目的要求是在输入的一串只包含左右括号的字符串中找到最长的一串左右括号匹配的合法子字符串。这题属于动态规划的题型,所以我们使用动态规划的方式来尝试一下。
首先看到这个题型就想到有一道是直接判断输入的字符串是否合法匹配的题目,使用的是栈进行压入和弹出就能解决。那同样的,我们这一道题目也可以利用栈这个数据结构来做题。基本的思路是将当读到左括号压入栈中,当读到右括号时,则到栈中弹出一个左括号;每弹出一个左括号,则对应的合法子字符串会有长度2的增加,如果没有左括号可以弹出,那就是该选择的字符串的长度最多只能到此。开始计算下一个合法子字符串的长度。
我们使用一个数组来存储字符串中读到对应位置时子符串能够具有的最大长度值。以输入“)()())"为例,我们创建一个长度为6并初始化值为0的数组SubStrLength[6],当子字符串可能继续增长时,数组对应位置的值继承上一位置的值,当能继续增长且此时出现了一次匹配成功,则在继承的基础上加2,字符串不可能继续增长时,数组对应位置的值则为0。
我们遍历字符串,首先数组第一个的长度自然会是0,即SubStrLength[0] = 0,只有一个字符没法实现匹配,读取的第一个字符是’)',不用压入栈中;
读取第二个字符,'(',将其压入栈中,继承上一位置的值,即SubStrLength[1] = SubStrLength[0] = 0;
读取第三个字符,为')',此时从栈中能找到'('进行匹配,则该位置上的值SubStrLength[2] = SubStrLength[1] + 2 = 2;
读取第四个字符,为'(',压入栈并继承上一位置的值 SubStrLength[3] = SubStrLength[2] = 2;
读取第五个字符,为')',此时从栈中能找到'('进行匹配,则该位置上的值SubStrLength[4] = SubStrLength[3] + 2 = 4;
读取第六个字符,为')',此时在栈中找不到'('匹配,则该子字符串无法继续再增长,该位置数值为SubStrLength[5] = 0;
所以我们找到了最长的子字符串长度为4。
这个方法看起来可以,但还不够全面,存在一个问题,当遇到”(()(()"时,这里我们可以看出最长子字符串长度为2,但由于第4个字符位置对应数组的值继承了上一位置的2,第5个位置的值又继续继承2,读到最后就变成了4。这里存在的问题是第4个位置上的'('并没有完成匹配,但是却传递了长度,所以我们要稍作改变,不将'('压入栈中,而是将对应字符的下标压入栈中,当我们把字符串遍历完后,我们要检查栈中是否还留有没有被匹配的左括号的下标,如果有的话,我们要将在这个下标之后的数组的值减掉该位置继承的值,并将该位置的值置为0。这样我们便真正解决了问题。
C++代码如下:
class Solution {
public:
int longestValidParentheses(string s) {
if (s.length() < 2)
return 0;
//我们用向量代替数组,方便使用函数找到最大值
vector<int> SubStrLength(s.length());
stack<int> st;
if (s[0] == '(') {
st.push(s[0]);
}
for (int i = 1; i < s.length(); ++i) {
if (s[i] == '(') {
st.push(i);
SubStrLength[i] = SubStrLength[i - 1];
} else {
if (st.empty()) {
SubStrLength[i] = 0;
} else {
st.pop();
SubStrLength[i] = SubStrLength[i - 1] + 2;
}
}
}
//减去栈中还存在的字符下边继承的长度
if (!st.empty()) {
int tail = SubStrLength.size();
while (!st.empty()) {
int front = st.top();
for (int i = front + 1; i < tail; ++i)
SubStrLength[i] -= SubStrLength[front];
st.pop();
SubStrLength[front] = 0;
tail = front;
}
}
return *max_element(begin(SubStrLength), end(SubStrLength));
}
};