[M贪心] lc678. 有效的括号字符串(括号问题+思维+模拟+dp)
Posted Ypuyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[M贪心] lc678. 有效的括号字符串(括号问题+思维+模拟+dp)相关的知识,希望对你有一定的参考价值。
1. 题目来源
括号问题题单:【宫水三叶】一题双解 :「动态规划」&「模拟」(附括号问题目录)
2. 题目解析
经典括号问题的套路:顺序遍历,记录左括号数量,遇到右括号减一,遍历过程中左括号不能为负值,并且最终左括号为 0 即为合法括号序列。
方法一:
在本题可以加入 *
这个可替代左、右括号的东西。可以扫描两次,分别判断:
- 正序扫描:将
*
认作左括号,遇到左括号、*
,都将cnt++
,遇见右括号就将cnt--
,期间cnt
不可为负值,最终要求cnt>=0
说明左括号足够。 - 逆序扫描:将
*
认作右括号,遇到右括号、*
,都将cnt++
,遇见左括号就将cnt--
,期间cnt
不可为负值,最终要求cnt>=0
说明右括号足够。 - 在任意一趟扫描过程中,若存在
cnt<0
则说明括号非法。
这个方法很简单直观,但是需要严格的证明。根据思路写的代码倒是可以 AC
。
方法二:
原先的括号序列问题,顺序扫描整个序列,只需要记录和维护左括号的一个确定数量即可。但是在此有 *
的存在,那么我们就需要维护一个左括号的取值区间,因为 *
影响了左括号的确定值:
- 记录左括号可能可用的最小值和最大值,记为
low, high
初始均为 0 - 遇见左括号:
low ++ , high ++
- 遇见右括号:
low -- , high --
,其中需要注意low >= 0
,即low
作为左括号可用的最小值不能为负值,所以要和 0 取max
。 - 遇见
*
:*
:表示左括号,则low ++ , high ++
*
:表示右括号,则low --, high --
*
:表示空,则low, high
保持不变- 那么
*
对左括号可用数量的影响就是low -- , high ++
,是对上面三个情况取的并集。
- 同时也需要注意,每次都需要保证
low=max(low, 0)
,保证左括号可用数量非负。且紧接着需要判断low>high
这种非法情况,因为[low, high]
相当于是左括号可用和数量的一个区间,该区间一定是非空的。其实也就是当右括号变多时,low、high
都小于 0 了,但是low
和 0 取max
就会导致low > high
自然也是非法情况了。 - 最后仅需判断左括号可用区间
[low, high]
是否能取到 0 即可。等价于判断low == 0
,因为low
不可大于 0,大于 0 代表有括号剩余,小于 0 的话就会被取max
到 0。
这个思路很巧妙,将一个变量转化为数学语言,就是一个波动的范围,思维难度有点高。
至于有双栈、dp 这两种写法,参考即可。感觉拓展性不大。
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度: O ( 1 ) O(1) O(1)
来自题解区黑鳌:
class Solution {
public:
bool checkValidString(string s) {
int left = 0, right = 0, size = s.size();
for(int i = 0; i < size; i++)
{
left += (s[i] == ')') ? -1 : +1; //从左向右看左括号能否有效
right += (s[size-1-i] == '(') ? -1 : +1; //从右向左看右括号能否有效
if(left < 0 || right < 0) return false;
}
return true;
}
};
class Solution {
public:
bool checkValidString(string s) {
int low = 0, high = 0;
for (char c : s) {
if (c == '(') low ++ , high ++ ;
else if (c == ')') low -- , high -- ;
else low -- , high ++ ;
low = max(low, 0);
if (low > high) return false;
}
return !low;
}
};
go 代码
func checkValidString(s string) bool {
low, high := 0, 0
for _, c := range s {
if (c == '(') {
low ++
high ++
} else if (c == ')') {
low --
high --
} else {
low --
high ++
}
low = max(low, 0)
if (low > high) {
return false
}
}
return low == 0
}
func max(a, b int) int {
if b > a {
return b
}
return a
}
以上是关于[M贪心] lc678. 有效的括号字符串(括号问题+思维+模拟+dp)的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode 678. 有效的括号字符串(贪心,动规) / 4. 寻找两个正序数组的中位数(二分,关键题) / 447. 回旋镖的数量