LeetCode: 678.有效的括号字符串|刷题打卡

247 阅读3分钟

一、题目描述

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

  1. 任何左括号 ( 必须有相应的右括号 )
  2. 任何右括号 ) 必须有相应的左括号 ( 。
  3. 左括号 ( 必须在对应的右括号之前 )
  4. * 可以被视为单个右括号 ) ,或单个左括号 ( ,或一个空字符串。
  5. 一个空字符串也被视为有效字符串。 示例 1:
输入: "()"
输出: True

示例 2:

输入: "(*)"
输出: True

示例 3:

输入: "(*))"
输出: True

二、思路分析

首先我们不看 * 号。
我们先来想想判断 有效的括号 例如:

如何证明 (()()) 是一个有效括号?
大家应该有发现一个 ) 抵消的一定是离它最近的一个 ( 。
那么我们来看看,如果我们遇到 ( 就 push 进数组中,遇到 ) 就从数组弹出末尾的 ( 出来
到最后如果数组的 length 为 0 是不是就代表它是一个有效的括号了?
那么大家也很容易的发现这种思路的特性时不时符合 栈 这个数据结构

那么我们已经知道了如何证明 有效的括号 了,那么这题就应该有些思路了。
首先我们应该用 栈 这种数据结构来做这道算法。
既然 * 是一种 万能卡 那么我们是否能用上述 有效的括号 的算法来先抵消 ,不够的时候再来用 * 来抵消呢?
既然如此那么 * 就要分作两个 栈 来存储了。
需要注意的是,用 * 抵消 要注意它门的位置,如果 * 的位置小于 也就是在 之前,那么就不能抵消,所以我们还要记录它们的位置信息,也就是下标。

三、AC 代码

栈结构

var checkValidString = function(s) {
    const left = [] // 左括号的栈
    const star = [] // 星号的栈
    
    // 遍历字符串
    for(let i = 0, l = s.length; i < l; i++) {
        if(s[i] === '(') left.push(i) // 如果当前字符串是 ( 就 push 到 left 栈中
        else if(s[i] === '*') star.push(i) // 如果当前字符串是 * 就 push 到 star 栈中
        else {
        	// 当前字符串是 ) 的时候
            
            // 优先判断 left 栈中是否还有元素,如果有就弹出一个抵消
            if(left.length) left.pop() 
            
            // 当 left 栈中没有元属的时候,就判断 star 栈中是否有元素,如果有就弹出一个抵消
            else if(star.length) star.pop() 
            
            // 无法抵消 ) 即代表该字符串是无效的
            else return false
        }
    }
	
    // 抵消完所有的 ) 后,我们要来抵消所有的 ( 和 * ,直到最少有一方抵消完
    while(left.length && star.length) {
        const _l = left.pop()
        const _s = star.pop()
        
        // 当星的位置在 ( 之前就代表该字符串是无效的
        if(_s < _l) return false
    }
	
    // 如果还有 ( 没抵消完即代表该字符串是无效的,否则就是有效的
    return left.length ? false : true
}

四、总结

该题的难点在于 * 的行为是什么。
如何来正确的使用 * 来抵消 是这题的关键。

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情