题目
给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
输入:s = “(()”
输出:2
解释:最长有效括号子串是 “()”
示例 2:
输入:s = “)()())”
输出:4
解释:最长有效括号子串是 “()()”
示例 3:
输入:s = “”
输出:0
- 方法一:栈,按右括号分割版
class Solution {
public:
int longestValidParentheses(string s) {
int maxlen = 0;
int start = 0;//当前有效括号序列的起始位置
stack<int> index;//储存左括号的下标
for(int i=0; i<s.size(); i++){
//如果此时没有左括号而直接出现了右括号,匹配失败
if(index.empty()&& s[i] ==')'){
start = i+1;
continue;
}
//左括号则入栈,右括号且栈非空则出栈并计算序列长度
if(s[i] =='('){
index.push(i);
}else{
index.pop();
//如果栈空则类似()(())
//栈非空则类似(()()
maxlen = index.empty() ? max(maxlen,i-start+1) : max(maxlen, i-index.top());
}
}
return maxlen;
}
};
-
时间复杂度O(n)
-
空间复杂度O(n)
-
思路
- 计算每一个以匹配成功的右括号为右端点的有效括号长,将它与当前的最大值比较,大于则更新。遍历字符串,以右括号为分类标准依次处理,遇到左括号则将它的下标入栈。
- 如果为右括号且此前没有左括号(栈空),则此前均无有效括号,有效括号匹配的起始位置start为此时下标加一,跳过该重循环进入下一层循环。
- 栈非空,此时遇到右括号则匹配成功,将此时的左括号出栈。如果此时栈空,则表示当前的最长有效括号已经处理完了,计算此时的长度为当前右括号的下标减去start加1.如果此时栈非空,则表示匹配成功的左括号为当前栈顶元素的下一个元素,计算此时的长度为当前右括号的下标减去栈顶元素。
- 计算每一个以匹配成功的右括号为右端点的有效括号长,将它与当前的最大值比较,大于则更新。遍历字符串,以右括号为分类标准依次处理,遇到左括号则将它的下标入栈。
-
方法二:栈,计算区间长度版
class Solution {
public:
int longestValidParentheses(string s) {
int maxlen = 0;
stack<int> stk;
stk.push(-1);//起始标志位
for(int i=0; i<s.size(); i++){
//左括号则入栈
if(s[i] == '('){
stk.push(i);
}else{
//右括号则出栈且计算长度
stk.pop();
if(stk.empty()){
stk.push(i);
}else{
maxlen = max(maxlen, i-stk.top());
}
}
}
return maxlen;
}
};
-
时间复杂度O(n)
-
空间复杂度O(n)
-
思路
- 根据匹配失败的括号来分割区间,计算每一次匹配成功时的有效括号长度
- 先将-1入栈,作为标志位。
- 如果为左括号则入栈,为右括号则出栈。如果栈非空,则表示匹配成功,此时的栈顶元素为成功匹配的左括号的前一个字符,计算此时的有效长度。如果栈空则表示匹配失败,将当前元素的下标作为标志位,入栈,表示下一次匹配的起始区间。
-
方法三:动态规划
class Solution {
public:
int longestValidParentheses(string s) {
int maxlen = 0;
vector<int> dp(s.size());
//dp[i]表示以i结尾的最长有效括号长度
//动态规划
for(int i=1; i<s.size(); i++){
//如果是左括号,有效括号长度为0
if(s[i] == '(') dp[i] = 0;
else{//如果为右括号,有两种情况
//它的前一个为左括号
if(s[i-1] == '(') dp[i] = i-2 >=0 ? dp[i-2] +2 : 2;
else{//它的前一个为右括号
if(i-dp[i-1]-1>=0 && s[i-dp[i-1]-1] == '(')
dp[i] = (i-dp[i-1]-1 == 0) ? 2+dp[i-1] :dp[i-dp[i-1]-2] +2 +dp[i-1];
}
}
maxlen = max(maxlen, dp[i]);
}
return maxlen;
}
};
-
时间复杂度O(n)
-
空间复杂度O(n)
-
思路
- dp[i]表示以s[i]结尾的最长有效括号长度
- 确定递推公式
- 如果s[i]==’(’,必不匹配,dp[i]=0
- 如果s[i]==’)’,分两种情况
- 如果s[i-1]==’(’,此时已经有一对匹配的括号了,dp[i] = dp[i-2]+2。表示新匹配的括号长度加上其子问题中匹配的括号长度。
- 如果s[i-1]==’)’,寻找s[i]所对应的左括号s[i-dp[i-1]-1],如果为左括号,则表示匹配成功dp[i] = dp[i-1]+2+dp[i-sdp[i-1]-2]。表示已匹配的括号长度2,加上前一个右括号匹配的括号长度dp[i-1],加上其子问题即这两个括号之前匹配的括号长度即dp[i-dp[i-1]-2]。
-
方法四:左右扫描法
class Solution {
public:
int longestValidParentheses(string s) {
int maxlen = 0;
int left=0, right = 0;
//两次扫描
//自左向右
for(int i=0; i<s.size(); i++){
if(s[i] == '(') left++;
else right++;
if(left == right){
maxlen = max(maxlen, 2*left);
}
if(left<right){
left = 0;
right = 0;
}
}
left = 0;
right = 0;
//自右向左
for(int i=s.size()-1; i>=0; i--){
if(s[i]=='(') left++;
else right++;
if(left == right){
maxlen = max(maxlen, 2*left);
}
if(left>right){
left = 0;
right = 0;
}
}
return maxlen;
}
};
- 时间复杂度O(n)
- 空间复杂度O(1)
- 思路
- 贪心。自左向右开始扫描,当括号匹配时左括号一定等于右括号,只有一种情况除外,即左括号一直大于右括号数,故扫描两遍。
- 自左向右扫描,每当左括号数目等于右括号数目时,计算匹配长度。如果右括号数量超过左括号数量,表示此前括号已经匹配结束,将括号数量重新设置为0。该方法在左括号始终比右括号多时无法计算有效括号序列,故扫描两边。
- 自右向左扫描,每当左括号数目等于右括号数目时,计算匹配长度。如果左括号数量超过右括号数量,表示此前括号已经计算完毕,将括号数量清零。