LeetCode 678.有效的括号字符串

题目

给定一个只包含三种字符的字符串:( ,) 和 *,写一个函数来检验这个字符串是否为有效字符串。有效字符串具有如下规则:

  • 任何左括号 ( 必须有相应的右括号 )。
  • 任何右括号 ) 必须有相应的左括号 ( 。
  • 左括号 ( 必须在对应的右括号之前 )。
  • 可以被视为单个右括号 ) ,或单个左括号 ( ,或一个空字符串。
  • 一个空字符串也被视为有效字符串。

示例

示例1

输入: “()”
输出: True

示例2

输入: “(*)”
输出: True

示例3

输入: “(*))”
输出: True

提示

  • 字符串大小将在 [1,100] 范围内。

题解

思路一


如果字符串中没有星号,则只需要一个栈存储左括号,在从左到右遍历字符串的过程中检查括号是否匹配。

在有星号的情况下,需要两个栈分别存储左括号和星号。从左到右遍历字符串,进行如下操作。

  • 如果遇到左括号,则将当前下标存入左括号栈。

  • 如果遇到星号,则将当前下标存入星号栈。

  • 如果遇到右括号,则需要有一个左括号或星号和右括号匹配,由于星号也可以看成右括号或者空字符串,因此当前的右括号应优先和左括号匹配,没有左括号时和星号匹配:

  • 如果左括号栈不为空,则从左括号栈弹出栈顶元素;

  • 如果左括号栈为空且星号栈不为空,则从星号栈弹出栈顶元素;

  • 如果左括号栈和星号栈都为空,则没有字符可以和当前的右括号匹配,返回 \text{false}false。

遍历结束之后,左括号栈和星号栈可能还有元素。为了将每个左括号匹配,需要将星号看成右括号,且每个左括号必须出现在其匹配的星号之前。当两个栈都不为空时,每次从左括号栈和星号栈分别弹出栈顶元素,对应左括号下标和星号下标,判断是否可以匹配,匹配的条件是左括号下标小于星号下标,如果左括号下标大于星号下标则返回 false。

最终判断左括号栈是否为空。如果左括号栈为空,则左括号全部匹配完毕,剩下的星号都可以看成空字符串,此时 ss 是有效的括号字符串,返回 true。如果左括号栈不为空,则还有左括号无法匹配,此时 ss 不是有效的括号字符串,返回 false。

代码实现

const checkValidString = function (s) {
	// 存储左括号下标的栈
    const stack1 = [];
    // 存储*号下标的栈
    const stack2 = [];
    for (let i = 0, len = s.length; i < len; i++) {
        const char = s[i];
        // 如果碰到的是右括号,则优先从左括号的栈中弹出一个,其次从*号的栈中弹出一个;否则表示没有与之匹配的符号,则返回true
        if (char === ")") {
            if (stack1.length) {
                stack1.pop()
            } else if (stack2.length) {
                stack2.pop();
            } else {
                return false;
            }
        }
        // 如果遇到左括号则把下标push进栈中(因为后面会用到下标比较)
        if (char === "(") {
            stack1.push(i)
        }
        // 如果遇到*号则把下标push进栈中
        if (char === "*") {
            stack2.push(i)
        }
    }
    // 依次把下标弹出栈进行比较,因为可能有这种情况"***("
    while (stack1.length || stack2.length) {
        const index1 = stack1.pop() ?? -1;
        const index2 = stack2.pop() ?? -1;
        if (index1 > index2) {
            return false;
        }
    }

    return true;
};

思路二

贪心算法
使用贪心的思想,可以将空间复杂度降到 O(1)O(1)。

从左到右遍历字符串,遍历过程中,未匹配的左括号数量可能会出现如下变化:

  • 如果遇到左括号,则未匹配的左括号数量加 1;

  • 如果遇到右括号,则需要有一个左括号和右括号匹配,因此未匹配的左括号数量减 1;

  • 如果遇到星号,由于星号可以看成左括号、右括号或空字符串,因此未匹配的左括号数量可能加 1、减 1 或不变。

基于上述结论,可以在遍历过程中维护未匹配的左括号数量可能的最小值和最大值,根据遍历到的字符更新最小值和最大值:

  • 如果遇到左括号,则将最小值和最大值分别加 1;

  • 如果遇到右括号,则将最小值和最大值分别减 1;

  • 如果遇到星号,则将最小值减 1,将最大值加 1。

任何情况下,未匹配的左括号数量必须非负,因此当最大值变成负数时,说明没有左括号可以和右括号匹配,返回 false。

当最小值为 0 时,不应将最小值继续减少,以确保最小值非负。

遍历结束时,所有的左括号都应和右括号匹配,因此只有当最小值为 0 时,字符串 s 才是有效的括号字符串。

代码实现

/**
 * @param {string} s
 * @return {boolean}
 */
const checkValidString = function (s) {
    let minCount = 0, maxCount = 0;
    const len = s.length;
    for (let i = 0; i < len; i++) {
        const c = s[i];
        // 如果遇到左括号最小值和最大值分别+1
        if (c === '(') {
            minCount++;
            maxCount++;
        } else if (c === ')') {
        	// 如果遇到右括号最小值和最大值分别-1
            minCount = Math.max(minCount - 1, 0);
            maxCount--;
            // 如果最大值小于0,说明没有左括号或者*号与右括号匹配,则返回false
            if (maxCount < 0) {
                return false;
            }
        } else {
        	// 如果遇到*号,最小值-1,最大值+1
            minCount = Math.max(minCount - 1, 0);
            maxCount++;
        }
    }
    return minCount === 0;
};

运行结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值