[M贪心] lc678. 有效的括号字符串(括号问题+思维+模拟+dp)

1. 题目来源

链接:678. 有效的括号字符串

括号问题题单:【宫水三叶】一题双解 :「动态规划」&「模拟」(附括号问题目录)

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
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值