LeetCode探索(140):921-使括号有效的最少添加

71 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情

题目

只有满足下面几点之一,括号字符串才是有效的:

  • 它是一个空字符串,或者
  • 它可以被写成 ABAB 连接), 其中 AB 都是有效字符串,或者
  • 它可以被写作 (A),其中 A 是有效字符串。

给定一个括号字符串 s ,移动一次,你就可以在字符串的任何位置插入一个括号。例如,如果 s = "()))" ,你可以插入一个开始括号为 "(()))" 或结束括号为 "())))" ,插入的位置是随意的。

返回 为使结果字符串 s 有效而必须添加的最少括号数

示例 1:

输入:s = "())"
输出:1

示例 2:

输入:s = "((("
输出:3

提示:

  • 1 <= s.length <= 1000
  • s 只包含 '('')' 字符。

思考

本题难度中等。题目其实不算难,难的是读懂题意!

首先是读懂题意。

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)。

参考