32. 最长有效括号
给定一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长的包含有效括号的子串的长度。
示例 1:
输入: "(()"
输出: 2
解释: 最长有效括号子串为 “()”
示例 2:
输入: ")()())"
输出: 4
解释: 最长有效括号子串为 “()()”
解题
将括号可能出现的顺序分类:
(1)‘()()()()’ 任意方向遍历可以得到
(2)‘((()))’ 任意方向遍历可以得到
(3)‘(((()’ 从右到左遍历可以得到
(4)‘()))))’ 从左到右遍历可以得到
暴力法:取出每一个子串进行计算(超时)
对s中每一个长度不小于2的子串进行从左到右遍历(用堆栈记录也行,用leftright’记录也行),找到最大长度的子串;
//堆栈记录
class Solution {
public:
int longestValidParentheses(string s) {
int n=s.size();
int MAXLEN=0;
for(int i=0;i<n-1;i++)
for(int j=2;j<=n-i;j++)
{
MAXLEN=max(MAXLEN,valid(s.substr(i,j)));
}
return MAXLEN;
}
int valid(string s) {
stack<char> match;
int n=s.size();
int len=0;
int MAXLEN=0;
for(int i=0;i<n;i++)
{
if(s[i]=='(') match.push('(');
else {
if(match.empty())
len=0;
else {
len+=2;
match.pop();
}
}
if(match.empty())
if(len>MAXLEN)
MAXLEN=len;
}
return MAXLEN;
}
};
//leftright记录
class Solution {
public:
int longestValidParentheses(string s) {
int n=s.size();
int MAXLEN=0;
for(int i=0;i<n-1;i++)
for(int j=2;j<=n-i;j++)
{
MAXLEN=max(MAXLEN,valid(s.substr(i,j)));
}
return MAXLEN;
}
int valid(string s) {
stack<char> match;
int n=s.size();
int MAXLEN=0;
int left=0;
int right=0;
for(int i=0;i<n;i++)
{
if(s[i]=='(') left++;
else right++;
if(left==right) MAXLEN=max(MAXLEN,right+left);
else if(left<right) left=right=0;
}
return MAXLEN;
}
};
动态规划:分类过多,难以想到
dp[i]表示以下标为i结尾的子串的有效括号长度;
(1)若s[i]== ‘(’
,则不会有以 ‘(’结尾的有效括号,都为0;
(2)若s [i] == ‘)’
且s[i-1]=='('
,dp[i]=2——当i>2时要加dp[i-2],因为前面可能也有有效括号;
(3)若 s [i] == ‘)’
且s[i-1] == ‘)’
且s[i-dp[i-1]-1]=='('
,说明该右括号有左括号匹配,dp[i]=dp[i-1]+2+dp[i-dp[i-1]-2];
,还要加上匹配的做括号之前的dp数量;
过程中记录最大的dp;
class Solution {
public:
int longestValidParentheses(string s) {
int n=s.size();
if(!n) return 0;
vector<int> dp(n,0);
int Max=0;
//以i结尾的有效括号子串长度
for(int i=1;i<n;i++)
{
if(s[i]==')'&&s[i-1]=='(') {
if(i>=2)
dp[i]=dp[i-2]+2;
else dp[i]=2;
}
if(s[i]==')'&&s[i-1]==')'&&i-dp[i-1]-1>=0&&s[i-dp[i-1]-1]=='(')
if(i-dp[i-1]-2>=0) dp[i]=dp[i-1]+2+dp[i-dp[i-1]-2];
else dp[i]=dp[i-1]+2;
if(dp[i]>Max) Max=dp[i];
}
return Max;
}
};
//简洁写法
class Solution {
public:
int longestValidParentheses(string s) {
int n=s.size();
if(!n) return 0;
vector<int> dp(n,0);
int Max=0;
//以i结尾的有效括号子串长度
for(int i=1;i<n;i++)
{
if(s[i]==')'&&s[i-1]=='(')
dp[i]= (i>=2?dp[i-2]+2:2);
if(s[i]==')'&&s[i-1]==')'&&i-dp[i-1]-1>=0&&s[i-dp[i-1]-1]=='(')
dp[i]=(i-dp[i-1]-2>=0?dp[i-1]+2+dp[i-dp[i-1]-2]:dp[i]=dp[i-1]+2);
if(dp[i]>Max) Max=dp[i];
}
return Max;
}
};
堆栈:关键在于要先将-1入栈
-1先入栈,遇到左括号,下标入栈;
遇到右括号弹出栈顶元素:
(1)若栈顶不为空,说明原先有左括号匹配,长度为右括号下标-当前栈顶的下标;
(2)若栈顶为空,说明该右括号无匹配,弹出的是-1,这时将该右括号下标入队,即从该下标开始往后计算匹配括号的长度;
class Solution {
public:
int longestValidParentheses(string s) {
stack<int> S;
S.push(-1);
int n=s.size();
if(!n) return 0;
int len=0;
for(int i=0;i<n;i++)
{
if(s[i]=='(') S.push(i);
else
{
S.pop();
if(!S.empty())
len=max(len,i-S.top());
else S.push(i);
}
}
return len;
}
};
记录left和right+无额外空间占用
从左到右记录左括号和右括号的数量,相等则记录len,右大于左则左右括号数量归0;(该遍历能得到‘()))’类型的括号);
从右到左记录右括号和做括号的数量,相等则记录len,左大于右则左右括号归0;(该遍历能得到‘((()’类型的括号数量);
class Solution {
public:
int longestValidParentheses(string s) {
int n=s.length();
int left=0;
int right=0; //从左到右遍历,记录各个括号的数量
int maxlen=0;
for(int i=0;i<n;i++)
{
if(s[i]=='(') left++;
else right++;
if(left==right) maxlen=max(maxlen,left+right);
if(right>left) left=right=0;
}
left=right=0;
for(int i=n-1;i>=0;i--)
{
if(s[i]=='(') left++;
else right++;
if(left==right) maxlen=max(maxlen,left+right);
if(right<left) left=right=0;
}
return maxlen;
}
};