给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
示例 2:
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
来源:力扣(LeetCode)
这一题真的太有意思了,栈,动态规划,还有括号计数,都是很方便很巧妙的方法,但我只会最low的暴力求每个子串的最大长度,哈哈😭
(一)
先说说括号计数的方法吧,顾名思义就是计数左右括号出现的次数
这种方法需要两次遍历,一次左到右,还有一次右到左,两者的顺序无所谓
先说说思路:
(从左往右遍历的时候)
遍历的时候用两个变量L和R记录左右括号出现的次数,一旦R>L,就说明当前遍历到的子串无效,为什么呢?因为多出来了一个左括号的话,只要右边还有右括号,就可以和左括号配对,还是有可能有效的,但是如果多出来一个右括号,那就没救了,没人和你配对啊,一直是多余的
然后当可能有人会问,只要左括号等于右括号,就一定有效吗?
对于一个完成了的序列来说,这个判断条件自然不成立,
比如
))((
但是我们是在一个一个的遍历啊,一开始就是右括号的话,R不就大于L了吗,那么将这个点作为开头的可能性都被我们抛弃了,所以在L == R的时候是可以保证括号的有效性的
缺陷:
虽然计数很方便,但是有一个致命的缺陷,那就是忽略了 L一直大于R的情况,比如
(((((((()
很明显这一串的最长有效括号为2,即最后面的()但是计数只能在L == R的时候保证括号的有效性,即只有在L = = R的时候才敢记录长度,却又只敢在R > L是舍弃当前位置,于是遍历上面的序列时,一直不舍弃,但又一直不敢记录长度,于是直到最后,R也没有等于L,
直到最后也没有记录长度,一直为0
但是这种缺陷也很容易解决,只要再来一次从右往左的遍历就行了!这就是上面所说的为什么要遍历两次的原因。
从右往左遍历的话就解决了L 一直大于 R的情况,两次遍历,互相弥补缺点
右往左的遍历其实和左往右差不多,改一下遍历顺序,改一下跳过条件就ok了
代码:
class Solution {
public:
int longestValidParentheses(string s) {
int L = 0, R = 0, maxLength = 0;
for(int i = 0;i<s.size();++i)//左往右
{
if(s[i]=='(') ++L;//记录左括号
else ++R;//记录右括号
//一旦两者相等,(就记录当前长度),不过依题意,我们要取最大长度
if(L == R) maxLength = max(maxLength,L+R);
else if(R>L) L = R = 0;//跳过
}
L = R = 0;//这一步别忘了,清空计数,为了方便阅读,我就没写在for里
for(int i = s.size()-1;i>=0;--i)//改一下,右往左
{
if(s[i]=='(') ++L;//一样的
else ++R;//一样的
if(L == R) maxLength = max(maxLength,L+R);//一样的
else if(R<L) L = R = 0;//改一下,
}
return maxLength;
}
};
(二)
提到括号,就不得不提到栈,这一题当然也可以用栈来解决
但是普通的括号匹配都是匹配整个序列,看中间是否会出错,出错就表明不是有效序列,
这一题的要求却是求最大的有效序列的长度,必须改变思路,,不过核心思路是不变的:
左括号就入栈,右括号就看栈里是否有剩余的左括号,如果有,则将左括号弹出栈
在这一题里,我们要始终保持栈顶的元素为最后一个的单身狗(没有能与之配对的括号)的下标,怎么理解呢?
在序列中如果有效子序列一直连续,那每次匹配完后,栈里都是空的,因为没有单身狗,但总会有来捣乱的单身狗,比如
()(()
上面的序列中,第三个括号 ’ ( '就是来捣乱的单身狗
但是具体情况比较复杂,单身狗可能没有,也可能是男’ ( ‘,也可能是女’ ) ’
怎么办呢?因为我们从左往右遍历的,我们先默认男性’ ( ‘都是单身狗,直到他的女朋友’ ) ‘来了,我们就把他踢出单身狗队列,而那些没有女朋友的或者说女朋友没来的,就默认他们是单身狗,这是对’ ( ’
然后是’ ) ’ ,除非队列中没有男性’ ( ‘,不然我们都默认它不是单身狗,不对它进行处理,反正’ ) ‘的下标也不会入栈,而如果我们发现在场没人了,那么这个’ ) '就是单身狗了,入栈
而求有效括号的最大长度,也变成了求最后一个单身狗后边的情侣人数
用下方代码的逻辑来解释一下:
首先遇见男性就入栈
而一旦遇见女性,先判断还有没有剩下的单身狗
如果没有了,就说明这个女性是现存唯一单身狗,入栈
然后如果有单身狗,那就判断是男还是女,如果是女,因为括号不搞同性恋,所以入栈
最后,就只有一种情况了,有一个男单身狗,可以配对,那就把男的踢出去,弹出栈
配对之后还是有两种情况
(一)栈空
如果一个单身狗都不剩了,就说明整个序列到此为止一直是有效括号,长度为当前括号下标+1
res = max ( res, i + 1 );
(二)不空
栈不空,就说明这个序列被单身狗打乱了,只能用下标的差来算出当前有效括号的长度,照理来说用下标算长度都需要+1,但这里配对成功的男性都被踢出去了,st.top()不是有效括号的开头,是更前面的下标,所以不用 i - st.top() +1 ,直接
res = max ( res, i - st.top() );
class Solution {
public:
int longestValidParentheses(string s) {
stack<int>st;
int res = 0;
for(int i = 0;i<s.size();++i)
{
if(s[i]=='(')st.push(i);//默认男的都是单身狗,入栈
else
{
if(st.empty() || s[st.top()]==')') st.push(i);//女单身狗的几种情况,入栈
else //成功匹配
{
st.pop();//匹配完弹出
if(st.empty())res = max(res,i+1);//如果是特殊情况,一直有效,长度就这样赋值
else res = max(res,i-st.top());//被打乱了就得用差值来求长度了
}
}
}
return res;
}
};
678. 有效的括号字符串
给定一个只包含三种字符的字符串:( ,) 和 *,写一个函数来检验这个字符串是否为有效字符串。有效字符串具有如下规则:
任何左括号 ( 必须有相应的右括号 )。
任何右括号 ) 必须有相应的左括号 ( 。
左括号 ( 必须在对应的右括号之前 )。
*可以被视为单个右括号 ) ,或单个左括号 ( ,或一个空字符串。
一个空字符串也被视为有效字符串。
来源:力扣(LeetCode)
思路:
沿用上一题的括号计数法,核心思路没啥变化
依旧是计数来判断当前遍历到的子串是否有效,如果无效,就加上*的数量,看看是不是有效,如果依旧无效,就说明没救了,直接return false吧
同理再反向遍历一次,同样计数+判断
上一题发现无效的时候就的抛弃当前位置,因为我们要找最长序列,这一题里发现无效的时候,可能还是有救的,因为有*,于是我们加上*看看有没有救
代码:
class Solution {
public:
bool checkValidString(string s) {
int L = 0,R = 0,ch = 0;
int size = s.size();
for(int i = 0;i<size;++i)
{
if(s[i]=='(')++L;
else if(s[i]==')')++R;
else if(s[i]=='*')++ch;
if(R>L)
{
if(L+ch<R)//如果算上*的数量L依旧比R小,就说明没救了,不然依旧有救
return false;
}
}
L = 0,R = 0,ch = 0;
for(int i = size-1;i>=0;--i)
{
if(s[i]=='(')++L;
else if(s[i]==')')++R;
else if(s[i]=='*')++ch;
if(L > R)
{
if(R+ch<L)
return false;
}
}
return true;
}
};