题目:

给定一个由 ’(‘ 和 ’)‘ 括号组成的字符串 S,我们需要添加最少的括号( ‘(‘ 或是 ’)‘,可以在任何位置),以使得到的括号字符串有效。

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

  • 它是一个空字符串,或者
  • 它可以被写成 AB (A 与 B 连接), 其中 A 和 B 都是有效字符串,或者
  • 它可以被写作 (A),其中 A 是有效字符串。
  • 给定一个括号字符串,返回为使结果字符串有效而必须添加的最少括号数。

  • 示例 1:

    输入:"())"
    输出:1
    
  • 示例 2:

    输入:"((("
    输出:3
    
  • 示例 3:

    输入:"()"
    输出:0
    
  • 示例 4:

    输入:"()))(("
    输出:4
    

来源:力扣(LeetCode)第921题

链接:https://leetcode-cn.com/problems/minimum-add-to-make-parentheses-valid

题目解析:

这题不是很难,比较容易就能想到,只要使用栈就能做出。在这里不讨论栈的方法,而是官方给出的一种更加巧妙和特别的方法,平衡法。

解题思路:

维护两个变量ans(answer)和bal(balance),ans是正常的结果,而bal则是题目可能发生的一种特殊情况。比如”)))(((“这种情况。这个答案应该是六,而不是0,因为右括号在前面,而左括号在后面,这就导致他们无法作为一对有效的括号。如果我们不用bal这个变量的话,应该是这样子的。

先遍历整个字符串,如果是左括号的话,ans加一,如果是右括号的话,ans减一。看似这样做没什么毛病,但是就像我刚才举得例子,当右括号在左括号的前面或者又括号比左括号多,都会出问题,那怎么办呢?这时,我们bal变量就能派上用场。(可能有人会说用个绝对值就行了,同样是上面那个例子,你会发现用绝对值还是不行!!!!)

还是刚才的思路,只是我们思考一下,如果右括号在前面或者右括号比左括号多的话,会导致ans的值为负数(出现了一次负数也是负数,不管它后面会不会变为正数)。一旦变成了负数(其实就是ans为-1,因为ans只有+1或-1的操作),那那个右括号一定是需要添加的括号,因为他一定是无效的括号。ans存的应该是需要添加的左括号和有效的两个括号,由于有效的两个括号一加一减没了,所以本质上就是存的无效左括号的次数,所以我们要把无效右括号的次数存在bal里面,同时为了不让右括号的-1导致左括号的次数不对,所以当ans值为-1时,bal要加一,同时ans也要加一,即相当于把ans的负数放在bal中。最后,我们只要将ans和bal相加即可得出答案。

简单理解:

维护ans和bal两个变量,ans存的是无效的左括号,同时当左括号的后面找到右括号后,左括号变为有效,那么ans就减一,如果右括号太多,左括号都是有效的,那么就有了无效的右括号,把他存在bal里面,同时将它离开ans,也就是ans+1(因为右括号是-1,+1相当于ans-(-1)),这样子一左一右即可以使两边平衡,最后的答案只要把ans和bal(无效左括号和无效右括号)加起来就行了。

Talk is cheap, show me the code.

平衡法:

class Solution(object):
    def minAddToMakeValid(self, S):
        ans = bal = 0
        for char in S:
            if char == '(':
                ans += 1
            else:
                ans -= 1
            if ans < 0:
                ans += 1
                bal += 1
        return ans + bal

顺便再附上栈的方法:

class Solution:
    def minAddToMakeValid(self, S: str) -> int:
        validStack = []
        for char in S:
            if char == ')' and validStack and validStack[-1] == "(":
                validStack.pop()
            else:
                validStack.append(char)
        return len(validStack)

复杂度分析:

平衡法 - 时间复杂度:O(n) n为S的长度(代码中的S) - 空间复杂度:O(1)

总结:

平衡法,我理解为把一个答案分成两类,就比如题目中把无效的括号分为无效左括号和无效右括号,分别求它们的答案,再把它们加在一起,有点像分治算法。