第一种:暴力法(超时)
第二种:动态规划(8ms)
第三种:栈(4ms)
第四种:双指针(8ms)
###题目
给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的连续子串的长度。
给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。
输入: ")()())"
输出: 4
解释: 最长有效括号子串为 "()()"
###思路
暴力法超时了,官方提供的解法,场面真尴尬,考虑给定字符串中所有可能的非空偶数长度子字符串,检查它是否是一个有效括号字符串序列(利用栈判断)。判断方法为,当遇到"("放在栈中,遇到")"看栈中是否有元素,如果有栈顶是否是左括号,如果栈中没有元素或者栈顶不是左括号,那么就不是有效括号串,遍历结束之后如果栈中还有元素,那么也不是合理的括号串。
例如有括号串:"((())",判断下列的非空偶数长度子字符串
(( --> 无效
(( --> 无效
() --> 有效,长度为 2
)) --> 无效
((()--> 无效
(())--> 有效,长度为 4
最长长度为 4
###代码
class Solution {
public:
bool panduan(string str)
{
stack<char>temp;
for(auto i:str)
{
if(i=='(')
temp.push(i);
else
{
if(!temp.empty()&&temp.top()=='(')
temp.pop();
else
return false;
}
}
return temp.empty();
}
int longestValidParentheses(string s) {
int maxlen = 0;
for (int i = 0; i < s.size(); i++) {
for (int j = i + 2; j <= s.size(); j+=2) {
if (panduan(s.substr(i, j-i))) {
maxlen = max(maxlen, j - i);
}
}
}
return maxlen;
}
};
###思路
动态规划的一个关键点就是确定dp矩阵如何更新。首先定义一个长度和字符串s长度相同的数组dp,dp[i]代表以s[i]为结尾的最长有效括号字符串长度,dp数组的初始值应该为0,一个字符不能组成有效的括号对,所以为0,然后从s[1]开始往后遍历并同时更新dp数组。
s[i]为左括号,那么不用更新dp[i],因为任何一个以左括号为结尾的字符串不可能是一个有效的字符串,所以依然为0.
s[i]为右括号,此时dp[i]的取值与s[i-1]有关,有两种情况:
一、s[i-1]为左括号,此时以s[i]为结尾的最长有效字符串的形式为..........(),只需要判断前方的.......的最大长度,其实这个最大长度就是dp[i-2],如果其存在,所以s[i-1]为左括号,s[i]为右括号的情况下,dp[i]=dp[i-2]+2。
二、s[i-1]为右括号,此时以s[i]为结尾的最长有效字符串可能存在的形式只有((..........)),其中........为一个有效括号字符串,然后只需要判断.......的长度就好了,其实.......的长度就是(.....)减去2,所以问题改变为判断(........)的长度,而(........)就是dp[i-1],然后还有一个问题,可能存在这样的一种情况 :有效字符串+((..........)),所以最终的结果是还需要加上前面是否还有另外一条有效字符串,而那条字符串的长度可以用dp[i-dp[i-1]-2]来表示;
###代码
class Solution {
public:
int longestValidParentheses(string s) {
int res=0;
vector<int>dp(s.size(),0);
for(int i=1;i<s.size();++i)
{
if(s[i]==')')
{
if(s[i-1]=='(') dp[i]=(i>=2 ? dp[i-2]:0)+2;
else if ((i - dp[i - 1])> 0 && s[i - dp[i - 1] - 1] == '(')
{
dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
}
res = max(res,dp[i]);
}
}
return res;
}
};
###思路
第三种方法是利用了栈,但不是用栈判断是否是有效字符串。流程如下:
一、遇到左括号,将其下标压入栈中。
二、遇到右括号,检查栈中是否有元素,如果有,弹出此时的栈顶元素(一个左括号的下标),然后将此时的右括号的下标减去此下标就得到一个有效字符串的长度了,如果没有栈中没有元素,那么直接跳过此右括号。这样能够保证每次弹出的左括号与此时的右括号之间的那个字符串肯定是个有效字符串,然后这个长度再加上以它前一个字符为结尾的最长括号字符串就可以了,因为单独的右括号会被跳过,只有可以匹配的才计数。
###代码
class Solution {
public:
int longestValidParentheses(string s) {
int l=s.size();
int res=0;
vector<int>dp(l,0);
stack<int>st;
for(int i=0;i<l;++i)
{
if(s[i]=='(')
{
st.push(i);
}
else
{
if(!st.empty())
{
int pre=st.top()-1;
int len=0;
if(pre>=0) len=dp[pre]+i-st.top()+1;
else len=i-st.top()+1;
res=max(res,len);
dp[i]=len;
st.pop();
}
}
}
return res;
}
};
###思路
第四种方法是利用两个计数器left和right。
首先,从左到右遍历字符串,对于遇到的每个左括号,将left加一,对于遇到的每个右括号,将right加一。当left和right相等时,当前有效字符串长度为2*left,然后更新目前为止找到的最长子字符串。当right比left大时,将left和right同时变回0。
然后从右到左做一遍类似的工作,因为这里我们的判断为合理字符串的条件是left==right,但是left可能会一直大于right,比如"(((())",这样一直不能记性判定,但其实是有合理的字符串"(())",所以要换个方向再来一次。
###代码
class Solution {
public:
int longestValidParentheses(string s) {
int left = 0, right = 0, res = 0;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '(') {
left++;
} else {
right++;
}
if (left == right) {
res =max(res, 2 * left);
} else if (right >= left) {
left = right = 0;
}
}
left = right = 0;
for (int i = s.size() - 1; i >= 0; i--) {
if (s[i]== '(') {
left++;
} else {
right++;
}
if (left == right) {
res=max(res, 2 * left);
} else if (left >= right) {
left = right = 0;
}
}
return res;
}
};