一、题目描述
给定一个只包含三种字符的字符串:( ,) 和 *,写一个函数来检验这个字符串是否为有效字符串。有效字符串具有如下规则:
- 任何左括号
(必须有相应的右括号)。 - 任何右括号
)必须有相应的左括号(。 - 左括号
(必须在对应的右括号之前)。 *可以被视为单个右括号),或单个左括号(,或一个空字符串。- 一个空字符串也被视为有效字符串。 示例 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 春招闯关活动」, 点击查看 活动详情