持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情
题目
只有满足下面几点之一,括号字符串才是有效的:
- 它是一个空字符串,或者
- 它可以被写成
AB(A与B连接), 其中A和B都是有效字符串,或者 - 它可以被写作
(A),其中A是有效字符串。
给定一个括号字符串 s ,移动一次,你就可以在字符串的任何位置插入一个括号。例如,如果 s = "()))" ,你可以插入一个开始括号为 "(()))" 或结束括号为 "())))" ,插入的位置是随意的。
返回 为使结果字符串 s 有效而必须添加的最少括号数。
示例 1:
输入:s = "())"
输出:1
示例 2:
输入:s = "((("
输出:3
提示:
1 <= s.length <= 1000s只包含'('和')'字符。
思考
本题难度中等。题目其实不算难,难的是读懂题意!
首先是读懂题意。
You are given a parentheses string s. In one move, you can insert a parenthesis at any position of the string.
Return the minimum number of moves required to make s valid.
s[i] is either '(' or ')'.
也就是说,字符串只包含'('和')',我们可以添加'('或')',使得字符串中的括号是匹配的、有效的。
我们可以借助栈来解题。考虑到字符串的括号是有顺序的,需要先左括号再右括号,那么,我们可以遍历字符串的每个字符,并存入栈中。当栈尾是字符 ( 且当前字符是 ) 时,我们取出栈尾元素;否则,将当前元素存入栈中。遍历结束,栈中元素是不匹配的字符,此时,我们返回栈的长度即可。
考虑到我们需要遍历一次字符串,因此时间复杂度是O(n)。
解答
方法一:栈
/**
* @param {string} s
* @return {number}
*/
var minAddToMakeValid = function (s) {
let deque = []
for (let ch of s) {
if (deque.length > 0 && deque[deque.length - 1] ==='(' && ch === ')') {
deque.pop()
} else {
deque.push(ch)
}
}
return deque.length
}
复杂度分析:
- 时间复杂度:O(n),其中 n 为字符串的长度。我们需要遍历一次字符串。
- 空间复杂度:O(n)。最差情况下我们需要将字符串的所有字符都存入栈中。
方法二:贪心
除了使用栈进行匹配,我们还可以使用计数的方法,进行匹配时每次都取距离当前位置最近的括号,就可以确保括号是匹配的、有效的。
我们从左到右遍历字符串,在遍历过程中维护左括号的个数 leftCount 以及添加次数 ans。如果遇到左括号,则将左括号的个数加 1。如果遇到右括号,则需要和前面的左括号进行匹配。
/**
* @param {string} s
* @return {number}
*/
var minAddToMakeValid = function (s) {
let ans = 0, leftCount = 0
for (let i = 0; i < s.length; i++) {
const c = s[i]
if (c === "(") {
leftCount++
} else {
if (leftCount > 0) {
leftCount--
} else {
ans++
}
}
}
ans += leftCount
return ans
}
复杂度分析:
- 时间复杂度:O(n),其中 n 为字符串的长度。我们需要遍历一次字符串。
- 空间复杂度:O(1)。