找到平衡括号的最小编辑数量?

时间:2015-04-16 23:14:02

标签: algorithm parentheses

我对这个问题非常困惑。我知道使用递归和动态编程作为改进来找到2个字符串之间的编辑距离,但是对于如何使用这个来感到困惑。

不确定我的想法是否正确。但是我们有一串不平衡的括号说

String s = "((())))";

如何找到平衡括号的字符串,需要最少的编辑次数?

有人可以用一个例子来解释这个吗?

我仍然不确定我是否正确解释。

6 个答案:

答案 0 :(得分:5)

给定由左括号和右括号组成的字符串,我们被要求通过执行最少数量的删除,插入和替换操作来平衡它。

首先,让我们查看输入字符串,并将匹配的对不匹配的字符区分开来。我们可以通过执行以下算法来标记属于匹配对的所有字符:

  1. 找到未标记的'('后面跟未标记的')',两者之间标记为零个或多个。
  2. 如果没有这样的字符对,请终止算法。
  3. 否则,请标记'('和')'。
  4. 返回第1步。
  5. 标记对已经以零成本平衡,因此最佳行动方案是不再采取任何措施。

    现在让我们考虑一下没有标记的字符。请注意,没有未标记的'('后面跟未标记的')',否则该对将被标记。因此,如果我们从左到右扫描未标记的字符,我们会发现零或更多“'”'字符后跟零或更多'('字符。

    要平衡')的序列。对于字符,最好将每一个字段重写为'(',从第一个字母开始,排除最后一个。如果有奇数个')'删除最后一个字符是最佳的。

    对于'('字符的序列,最好将每隔一个字符重写为'),从第二个开始。如果有剩余的'('字符,我们会将其删除。 以下Python代码实现上述步骤并显示中间结果。

    def balance(s):  # s is a string of '(' and ')' characters in any order
      n = len(s)
      print('original string: %s' % s)
    
      # Mark all matched pairs
      marked = n * [ False ]
      left_parentheses = []
      for i, ch in enumerate(s):
        if ch == '(':
          left_parentheses.append(i)
        else:
          if len(left_parentheses) != 0:
            marked[i] = True
            marked[left_parentheses.pop()] = True
    
      # Display the matched pairs and unmatched characters.
      matched, remaining = [], []
      for i, ch in enumerate(s):
        if marked[i]:
          matched.append(ch)
          remaining.append(' ')
        else:
          matched.append(' ')
          remaining.append(ch)
      print('  matched pairs: %s' % ''.join(matched))
      print('      unmatched: %s' % ''.join(remaining))
    
      cost = 0
      deleted = n * [ False ]
      new_chars = list(s)
    
      # Balance the unmatched ')' characters.
      right_count, last_right = 0, -1
      for i, ch in enumerate(s):
        if not marked[i] and ch == ')':
          right_count += 1
          if right_count % 2 == 1:
            new_chars[i] = '('
            cost += 1
            last_right = i
      if right_count % 2 == 1:      # Delete the last ')' if we couldn't match it.
        deleted[last_right] = True  # The cost was incremented during replacement.
    
      # Balance the unmatched '(' characters.
      left_count, last_left = 0, -1
      for i, ch in enumerate(s):
        if not marked[i] and ch == '(':
          left_count += 1
          if left_count % 2 == 0:
            new_chars[i] = ')'
            cost += 1
          else:
            last_left = i
      if left_count % 2 == 1:      # Delete the last '(' if we couldn't match it.
        deleted[last_left] = True  # This character wasn't replaced, so we must
        cost += 1                  # increment the cost now.
    
      # Display the outcome of replacing and deleting.
      balanced = []
      for i, ch in enumerate(new_chars):
        if marked[i] or deleted[i]:
          balanced.append(' ')
        else:
          balanced.append(ch)
      print('        balance: %s' % ''.join(balanced))
    
      # Display the cost of balancing and the overall balanced string.
      print('           cost: %d' % cost)
      result = []
      for i, ch in enumerate(new_chars):
        if not deleted[i]:  # Skip deleted characters.
          result.append(ch)
      print('     new string: %s' % ''.join(result))
    
    
    balance(')()(()())))()((())((')
    

    对于测试用例')()(()())))()((())((',输出如下。

    original string: )()(()())))()((())((
      matched pairs:  ()(()())  () (())
          unmatched: )        ))  (    ((
            balance: (        )   (    )
               cost: 4
         new string: (()(()()))()((()))
    

答案 1 :(得分:2)

这个想法很简单:

  • 查找最后一个字符串,其中包含无法打开和关闭的括号。请记住,在最后一个字符串中,将使用小括号,然后打开括号。
  • 现在我们必须分别编辑开括号和左括号。
  • 例如:for close bracket:
  •               (1)如果长度均匀:
    min edit to balance将改变半闭括号以打开括号。
    所以minEdit = closeBracketCount / 2
    。 (2)如果是奇数长度:
    min edit to balance将在上面的步骤1中执行并删除剩余的1个括号。
    所以minEdit = closeBracketCount / 2 + 1

  • 对于开括号:
    (1)如果长度均匀:
    min edit to balance将更改半开括号以关闭括号。
    所以minEdit = openBracketCount / 2。
    (2)如果是奇数长度:
    min edit to balance将在上面的步骤1中执行并删除剩余的1个括号。
    所以minEdit = openBracketCount / 2 + 1

    以下是正在运行的代码:http://codeshare.io/bX1Dt
    让我知道你的想法。

  • 答案 2 :(得分:1)

    虽然这个有趣的问题可以通过评论中提到的动态编程来解决,但它有一个更简单的解决方案。你可以用贪心算法解决它。

    这种贪婪算法的想法来自于我们如何检查括号表达式的有效性。您将counter设置为0并遍历括号字符串,在"("和和#34;)中添加1;"。如果计数器始终保持在0或0并且在0结束,则表示您有一个有效的字符串。

    这意味着如果我们在遍历时遇到的最低值是-maxi,我们需要准确添加-maxi"("在开始时。调整最终计数器值以添加"( "并添加足够的")"在结尾处以0结束。

    以下是算法的伪代码:

    counter = 0
    mini = 0
    for each p in string:
      if p == "(":
        counter++
      else:
        counter--
    
      mini = min(counter, mini)
    
    add -mini "(" at the start of the string
    counter -= mini
    add counter ")" at the end of the string
    

    答案 3 :(得分:0)

    我厌倦了用DP算法解决问题,它通过了我自己编写的一些测试用例。如果您认为这是正确的,请告诉我。

    P(i,j)成为使字符串S[i..j]达到平衡的最低编辑次数。

    S[i]等于S[j]时,最低编辑数明显为P(i+1,j-1)

    有一些选项可以在S[i] != S[j]时使字符串保持平衡,但最后我们可以添加'('到i的前面或')&# 39;在j的末尾,或删除i或j处的括号。在所有这些情况下,最低编辑数为min{P(i+1, j), P(i, j-1)} + 1

    因此,我们有以下DP公式:

    P(i,j) = 0 if i > j
           = P(i + 1, j - 1) if S[i] matches S[j] OR S[i] and S[j] are not parenthesis
           = min{P(i + 1, j), P(i, j - 1)} + 1
    

    答案 4 :(得分:0)

    我会使用堆栈来有效地平衡它们。这是python代码:

    a=['(((((','a(b)c)','((())))',')()(()())))()((())((']
    
    
    def balance(s):
      st=[]
    
      l=len(s)
      i=0
    
      while i<l:
        if s[i]=='(':
          st.append(i)
        elif s[i]==')':
          if st:
            st.pop()
          else:
            del s[i]
            i-=1
            l-=1
    
        i+=1
    
      while st:
        del s[st.pop()]
    
      return ''.join(s)
    
    for i in a:
      print balance(list(i))
    

    <强>输出

    Empty 
    a(b)c
    ((()))
    ()(()())()(())
    

    答案 5 :(得分:0)

     //fisher
       public int minInsertions(String s) {
            Stack<Character> stack = new Stack<>();
            int insertionsNeeded = 0;
            for (int i = 0; i < s.length(); i++) {
                char c = s.charAt(i);
                if (c == '(') {
                    if (stack.isEmpty()) {
                        stack.add(c);
                    } else {
                        if (stack.peek() == ')') {
                            //in this case, we need to add one more ')' to get two consecutive right paren, then we could pop the one ')' and one '(' off the stack
                            insertionsNeeded++;
                            stack.pop();
                            stack.pop();
                            stack.add(c);
                        } else {
                            stack.add(c);
                        }
                    }
                } else if (c == ')') {
                    if (stack.isEmpty()) {
                        //in this case, we need to add one '(' before we add this ')' onto this stack
                        insertionsNeeded++;
                        stack.add('(');
                        stack.add(c);
                    } else {
                        if (stack.peek() == ')') {
                            //in this case, we could pop the one ')' and one '(' off the stack
                            stack.pop();
                            stack.pop();
                        } else {
                            stack.add(c);
                        }
                    }
                }
            }
            if (stack.isEmpty()) {
                return insertionsNeeded;
            } else {
                while (!stack.isEmpty()) {
                    char pop = stack.pop();
                    if (pop == '(') {
                        insertionsNeeded += 2;
                    } else {
                        insertionsNeeded++;
                        stack.pop();
                    }
                }
                return insertionsNeeded;
            }
        }
    }