LeetCode32-最长有效括号
32. 最长有效括号:
给你一个只包含
'('
和')'
的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
示例 2:
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
示例 3:
输入:s = ""
输出:0
提示:
- 0 <= s.length <= 3 * 10^4
- s[i] 为 ‘(’ 或 ‘)’
解题思路1:
动态规划三步走:
(1)状态定义: 定义dp[i] 表示以下标 i 字符结尾的最长有效括号的长度。
(2)初始化: dp[0]=0
(3)状态转移:
判读当前的字符是左括号还是右括号。
如果是做左括号dp[i]=0;
如果是右括号就去匹配对应位置是否是左括号,即对应位置为减去前面匹配的括号数量再减1.
代码如下:
class Solution {
const static int MAXN=3e4+5;
int dp[MAXN]; //表示当前位置能匹配的长有效括号
public:
int longestValidParentheses(string s) {
s="@"+s;
int len=s.size();
//cout<<s<<endl;
stack<char>my_stack;
int ans=0;
dp[0]=0;
for(int i=1;i<len;++i) {
if(s[i]=='(')
dp[i]=0;
else{
int idx=i-dp[i-1]-1;
//cout<<"idx="<<idx<<endl;
if(s[idx]=='(')
dp[i]=dp[idx-1]+dp[i-1]+2;
else
dp[i]=0;
}
//cout<<"dp["<<i<<"]="<<dp[i]<<endl;
ans=max(ans,dp[i]);
}
return ans;
}
};
解题思路2:
栈的应用。
将符号和匹配了的数量一起入栈。数字就代表完全匹配的括号长度,必为偶数。
如果是左括号则直接入栈,如果是右括号则进行匹配。
注意:栈中数字可能匹配两次,即((6(2
这时来了一个右括号则需要匹配两个数字。
代码如下:
class Solution {
public:
int longestValidParentheses(string s) {
stack<pair<char,int> >my_stack;
pair<char,int>now;
int len=s.size();
//cout<<"len="<<len<<endl;
int ans=0;
for(int i=0;i<len;++i) {
if(s[i]=='(')
my_stack.push(make_pair('(',1));
else {
//printf("i=%d,%c\n",i,s[i]);
if(my_stack.empty()==true) {
my_stack.push(make_pair(')',0));
continue;
}
int tmp=0;
now=my_stack.top();
//printf("now=%d\n",now.second);
int flag=1; //用于判断这个符号是否被匹配掉
if(now.second==0) {
my_stack.push(make_pair(')',0));
flag=0;
}
else if(now.second==1) {
my_stack.pop();
tmp+=2;
flag=0;
if(!my_stack.empty()) {
now=my_stack.top();
if(now.second>1) {
my_stack.pop();
tmp+=now.second;
}
}
my_stack.push(make_pair('*',tmp));
}
else {
my_stack.pop();
tmp+=now.second;
if(!my_stack.empty()) {
now=my_stack.top();
if(now.second==1) {
my_stack.pop();
tmp+=2;
flag=0;
}
}
//前面还有数字可以匹配
if(!my_stack.empty()) {
now=my_stack.top();
if(now.second>1) {
my_stack.pop();
tmp+=now.second;
}
}
my_stack.push(make_pair('*',tmp));
}
if(flag)
my_stack.push(make_pair(')',0));
//cout<<"tmp="<<tmp<<endl;
ans=max(ans,tmp);
}
//show_s(my_stack);
}
return ans;
}
};
解题思路3:
栈的应用,在解题思路2的基础上进行优化。
栈底元素为当前已经遍历过的元素中「最后一个没有被匹配的右括号的下标」。
左括号下标直接入栈,右括号则进行匹配:
- 栈不为空则匹配成功
- 栈为空则说明唯有左括号与其匹配,则将右括号下标入栈
代码如下:
class Solution {
public:
int longestValidParentheses(string s) {
stack<int>my_stack;
int len=s.size();
my_stack.push(-1);
int ans=0;
for(int i=0;i<len;++i) {
if(s[i]=='(')
my_stack.push(i);
else {
my_stack.pop();
if(my_stack.empty())
my_stack.push(i);
else
ans=max(ans,i-my_stack.top());
}
}
return ans;
}
};
解题思路4:
正向逆向结合
贪心地考虑以当前字符下标结尾的有效括号长度.每次当右括号数量多于左括号数量的时候之前的字符我们都扔掉不再考虑,重新从下一个字符开始计算
但这样会漏掉一种情况,就是遍历的时候左括号的数量始终大于右括号的数量,即 (() ,这种时候最长有效括号是求不出来的。
解决的方法也很简单,我们只需要从右往左遍历用类似的方法计算即可。
代码如下:
class Solution {
public:
int longestValidParentheses(string s) {
int len=s.size();
int left=0,right=0;
int ans=0;
for(int i=0;i<len;++i) {
if(s[i]=='(')
++left;
else {
++right;
if(left==right)
ans=max(ans,right<<1);
else if(right>left)
left=right=0;
}
}
left=0;
right=0;
for(int i=len-1;i>=0;--i) {
if(s[i]==')')
++right;
else {
++left;
if(left==right)
ans=max(ans,right<<1);
else if(left>right)
left=right=0;
}
}
return ans;
}
};