leetcode 32. 最长有效括号 hard
题目描述:
给定一个只包含 '(' 和 ')' 的字符串,找出最长的包含有效括号的子串的长度。
示例 1:
输入: "(()"
输出: 2
解释: 最长有效括号子串为 "()"
示例 2:
输入: ")()())"
输出: 4
解释: 最长有效括号子串为 "()()"
解题思路:
方法一: nb解法
一个括号序列有两个很重要的性质:
- 不管我们怎么找有效的子串,一个特定的左括号跟它匹配的右括号总是不变的。比如()()(())) 不管怎么找,标红的两个括号总是匹配在一起的。 并且(很重要) 在总的序列一个没法找到匹配的左括号or右括号,你在任何子串中也无法找到一个跟他匹配的右括号or左括号,比如上面例子的最后一个右括号
- 括号序列合法 等价于 所有前缀和>=0,且总和等于0(把左括号看出1,右括号看成-1)(重要)
所以我们
简单的说就是这一部分如果出现问题了,那肯定最后一位是右括号,并且是因为这个右括号没人匹配导致的,那么这一部分就可以直接放弃了,因为不会被匹配的右括号永远不会被匹配。
不过从左到右遍历 只能统计总的右括号数目大于等于左括号的情况,但是对于(((()) 这种却无能为力,所以我们需要从右边到左边再来一次(注意, 反着做把右括号看成1,左括号看成-1)
方法二:dp
这道题最重要的就是教会我,不管乱七八糟的,一定要记住数组别越界(还有假如求什么最长的,先想dp)
方法三:
用栈来做, 其实更推荐用这个方法。
代码:
//nb解法
class Solution {
public:
int longestValidParentheses(string s) {
int start=0;
int cnt=0; // 前缀和
int res=0;
// 正着做
for(int i=0;i<s.size();++i){
if(s[i]=='(') cnt++;
else{
cnt--;
if(cnt<0){ start=i+1;cnt=0;}
else if(cnt==0) res=max(res,i-start+1);
}
}
// 反着做
int end=s.size()-1;
cnt=0;
for(int i=end;i>=0;--i){
if(s[i]==')') cnt++; // 反着做把右括号看成1,左括号看成-1
else{
cnt--;
if(cnt<0) {end=i-1;cnt=0;}
else if(cnt==0) res=max(res,end-i+1);
}
}
return res;
}
};
//dp
class Solution {
public:
int longestValidParentheses(string s) {
vector<int> dp(s.size(),0);
int maxlen=0;
// 以每个位置为尾的最长有效括号的长度
for(int i=1;i<s.size();++i){
if(s[i]==')' && s[i-1]=='(')
dp[i]= (i>=2?dp[i-2]:0)+2;
else if(s[i]==')' && s[i-1]==')'){
// 别管乱七八糟的 别越界就行
if(i-1-dp[i-1]>=0 && s[i-1-dp[i-1]]=='(')
dp[i]=dp[i-1] + 2+ (i-2-dp[i-1]>=0?dp[i-2-dp[i-1]]:0); // 别忘了加上 最后一个表达式
}
maxlen=max(maxlen,dp[i]);
}
return maxlen;
}
};
栈作法:
class Solution {
public:
int longestValidParentheses(string s) {
stack<int> stk;
int start = 0;
int ret = 0;
for (int i = 0; i < s.size(); i++){
if (s[i] == '('){
stk.push(i);
} else if (stk.empty()) {
start = i+1;
} else {
stk.pop();
int one = stk.empty()? i-start+1: i-stk.top();
ret = max(ret, one);
}
}
return ret;
}
};