1 题目
给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。
返回所有可能的结果。答案可以按 任意顺序 返回。
示例 1:
输入: s = "()())()"
输出: ["(())()","()()()"]
示例 2:
输入: s = "(a)())()"
输出: ["(a())()","(a)()()"]
示例 3:
输入: s = ")("
输出: [""]
提示:
1 <= s.length <= 25s由小写英文字母以及括号'('和')'组成s中至多含20个括号
2 分析
从题目提供的信息可以知道:
- 字符串
s里除了括号可能还有其他字符 - 我们要删除无效的括号,但是需要是数量最小的操作
2.1 怎么找出所有结果
首先第一个想到的办法就是暴力法,把所有可能的情况都枚举一遍,那么每次进入枚举结果的字符应当符合这个逻辑:
- 如果当前字符为 '(',那么最终的结果就是:加 / 不加 两种情况
- 如果当前字符为 '(',那么最终的结果也是:加 / 不加 两种情况
- 否则,当前字符只能加到最终的结果里
2.2 验证最终结果是否合法
2.2.1 方案一、使用栈来进行判断
众所周知,括号可以用栈来进行匹配,只是我们在这个场景下需要处理「非括号」的字符,这里直接给出代码:
const isParentheses = (s) => {
return s === '(' || s === ')'
}
const isValidParentheses = (s, stack = []) => {
let i = 0
while (i < s.length) {
if (!stack.length) {
stack.push(s[i])
i++
continue
}
const top = stack[0]
if (top === '(') {
if (s[i] === ')') {
stack.shift()
} else if (s[i] === '(') {
stack.push(s[i])
}
} else if (top === ')' || s[i] === ')') {
// 如果最顶部是 ),或者最顶部为非括号并且下一个为 ), 说明不是合法括号
return false
} else if (s[i] === '(') {
stack.shift()
stack.push(s[i])
}
i++
}
return stack.every(item => !isParentheses(item)) ? true : !stack.length
}
2.2.2 方案二、使用计数法进行过滤
- 如果有一个 '(' 就进行执行
left + 1 - 如果有一个 ')' 就执行
right + 1 - 如果出现
left - right < 0,那么说明当前的字符串不合法
其实这个逻辑也很好理解,如果出现了 left - right < 0,那么就说明当前的字符串是以下这些组合中的其中一种:
()))
即:要么是 '(' 数量不够,要么就是只有 ')'
2.3 剪枝 & 去重
2.3.1 剪枝
剪枝很好理解,就是我们在递归过程中规避掉已经出现过的值,避免重复计算的手段。 比如在这个场景下,每次递归里当前的字符串就是可以用 map 来进行剪枝的。
2.3.2 去重
在这个场景下,即使进行剪枝了也避免不了最后出现重复的结果,举个例子:
- )()()) => ()()) => ()()
- )()()) => ()()) => ()()
在这个例子中,当前字符为 ()()) 时就会出现 2 个重复的结果,因此在返回最终结果时还需要做一次去重
3 代码
/**
* @param {string} s
* @return {string[]}
*/
var removeInvalidParentheses = function (s) {
const dfs = (acc, i, left, right) => {
let step = s.length - (left + right)
if (left - right < 0) return
if (i === s.length) {
const isValid = left === right && minStep >= step
if (isValid) {
minStep = Math.min(minStep, step)
result.push(acc)
}
return
}
if (map[acc]) return
if (s[i] === '(') {
// 遇到左括号,尝试加 / 不加
dfs(acc + s[i], i + 1, left + 1, right)
dfs(acc, i + 1, left, right)
}
else if (s[i] === ')') {
// 遇到右括号,尝试加 / 不加
dfs(acc + s[i], i + 1, left, right + 1)
dfs(acc, i + 1, left, right)
}
// 遇到其他字符,直接保留
else dfs(acc + s[i], i + 1, left, right)
map[acc] = true
}
const result = []
const map = {}
let minStep = Infinity
dfs("", 0, 0, 0)
return result.length ? [...new Set(result)] : ['']
};
- 时间复杂度:
- 空间复杂度: