【算法】有效的括号字符串

难度:中等

题目:

给你一个只包含三种字符的字符串,支持的字符类型分别是 ‘(’、‘)’ 和 ‘*’。请你检验这个字符串是否为有效字符串,如果是 有效 字符串返回 true 。

有效 字符串符合如下规则:

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

示例 1
输入:s = “()”
输出:true

示例 2
输入:s = “(*)”
输出:true

示例 3
输入:s = “(*))”
输出:true

提示
1 <= s.length <= 100
s[i] 为 ‘(’、‘)’ 或 ‘*’

解题思路:

  1. 初始化两个计数器:定义两个变量,leftMin和leftMax,初始值均为0。leftMin用来记录在最不利情况下(即尽量多使用星号作为左括号)剩余未匹配的左括号最小数量,而leftMax用来记录在最有利情况下(即尽量多使用星号作为右括号)剩余未匹配的左括号最大数量。
  2. 遍历字符串:遍历输入字符串s的每一个字符。
  • 遇到左括号(时,leftMin和leftMax都增加1,因为左括号明确增加了待匹配的数量。
  • 遇到右括号)时,leftMin和leftMax都减1,表示消耗一个匹配的机会。但是,为了确保leftMin不会变成负数(表示右括号多于左括号),如果leftMin大于0才减1。同时,如果leftMax减到0以下,说明右括号太多了,直接返回false,因为即使所有星号都作为右括号也不够匹配。
  • 遇到星号*时,它既可以作为左括号也可以作为右括号,因此leftMax增加1(考虑它作为右括号),同时为了最坏情况(尽量让星号充当左括号),leftMin减1(但同样要确保leftMin不小于0,防止过度消耗左括号)。
  1. 检查结果:遍历完成后,如果leftMin等于0,说明即使在最不利情况下所有的左括号也得到了匹配,返回true;否则,返回false。

为什么这种方法有效?
这种方法巧妙地利用了星号的双重角色,通过维护两个边界(最小和最大可能的左括号匹配数),有效地判断了在任何合理的星号分配策略下,字符串中的括号是否可以被正确匹配。这种方法避免了复杂的回溯或动态规划,实现了线性时间复杂度的解决方案。

JavaScript 实现

function checkValidString(s) {
    let leftMin = 0, leftMax = 0;

    // 前向遍历,同时跟踪最小和最大可能的左括号数量
    for (let char of s) {
        if (char === '(') {
            leftMin++; // 左括号最小计数增加
            leftMax++; // 左括号最大计数增加
        } else if (char === ')') {
            if (leftMin > 0) leftMin--; // 优先减少最小左括号计数,模拟最优情况
            leftMax--; // 减少最大左括号计数
            if (leftMax < 0) return false; // 如果最大左括号计数小于0,说明右括号过多
        } else { // char === '*'
            if (leftMin > 0) leftMin--; // 可选减少最小左括号计数
            leftMax++; // 星号也可以作为右括号,所以最大左括号计数可以增加
        }
    }

    // 最终检查:最小左括号计数必须为0,确保所有左括号至少能被匹配
    return leftMin === 0;
}

// 测试例子
console.log(checkValidString("(*))")); // 应该输出true
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值