删除无效的括号 - 深度优先搜索的应用

1. 题目描述

删除最小数量的无效括号,使得输入的字符串有效,返回所有可能的结果。
说明: 输入可能包含了除()以外的字符。
示例 1:

输入: "()())()"
输出: ["()()()", "(())()"]

示例 2:

输入: "(a)())()"
输出: ["(a)()()", "(a())()"]

2. 题解

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

class Solution {

  private Set<String> validExpressions = new HashSet<String>();
  private int minimumRemoved;

  private void reset() {
    this.validExpressions.clear();
    this.minimumRemoved = Integer.MAX_VALUE;
  }

  private void recurse(
      String s,
      int index,
      int leftCount,
      int rightCount,
      StringBuilder expression,
      int removedCount) {

    // If we have reached the end of string.
    if (index == s.length()) {

      // If the current expression is valid.
      if (leftCount == rightCount) {

        // If the current count of removed parentheses is <= the current minimum count
        if (removedCount <= this.minimumRemoved) {

          // Convert StringBuilder to a String. This is an expensive operation.
          // So we only perform this when needed.
          String possibleAnswer = expression.toString();

          // If the current count beats the overall minimum we have till now
          if (removedCount < this.minimumRemoved) {
            this.validExpressions.clear();
            this.minimumRemoved = removedCount;
          }
          this.validExpressions.add(possibleAnswer);
        }
      }
    } else {

      char currentCharacter = s.charAt(index);
      int length = expression.length();

      // If the current character is neither an opening bracket nor a closing one,
      // simply recurse further by adding it to the expression StringBuilder
      if (currentCharacter != '(' && currentCharacter != ')') {
        expression.append(currentCharacter);
        this.recurse(s, index + 1, leftCount, rightCount, expression, removedCount);
        expression.deleteCharAt(length);
      } else {

        // Recursion where we delete the current character and move forward
        this.recurse(s, index + 1, leftCount, rightCount, expression, removedCount + 1);
        expression.append(currentCharacter);

        // If it's an opening parenthesis, consider it and recurse
        if (currentCharacter == '(') {
          this.recurse(s, index + 1, leftCount + 1, rightCount, expression, removedCount);
        } else if (rightCount < leftCount) {
          // For a closing parenthesis, only recurse if right < left
          this.recurse(s, index + 1, leftCount, rightCount + 1, expression, removedCount);
        }

        // Undoing the append operation for other recursions.
        expression.deleteCharAt(length);
      }
    }
  }

  public List<String> removeInvalidParentheses(String s) {

    this.reset();
    this.recurse(s, 0, 0, 0, new StringBuilder(), 0);
    return new ArrayList(this.validExpressions);
  }
}

根据题意,如果当前字符不是左括号(或者右括号),就将其直接添加到解决方案字符串中,因为它不影响括号的有效性。
对于左括号和右括号,有两种情况,即删除和不删除。

如果当前字符是左括号或者右括号,就将其删除,接着看下一个字符。当递归删除到最后一个字符时,然后回溯将最后一个字符添加到解决方案字符串中(不删除)。
这里用leftCountrightCount记录考虑的左括号数和右括号数,由于是最后一个字符,且前面的字符被递归删除了,所以不能形成有效的符号。
接着删除解决方案字符串中最后一个字符(一会儿还会递归到最后一个字符,但已经是另一个情况了),继续回溯将倒数第二个字符添加到解决方案字符串中。如果倒数第二个字符是左括号,leftCount加1,接着看最后一个字符,如果最后一个字符是右括号,rightCount加1,这时递归到最后一个字符,且左右括号数相等,记录此时删除括号的数量minimumRemoved以及对应的有效括号validExpressions

通过深度优先搜索遍历完所有可能的情况,得到minimumRemoved的全局最小值。注意到validExpressions是一个集合,表示可能存在删除括号数相同的多种情况,即删除括号数相同,得到不同的有效括号,这些有效括号都加到集合validExpressions中。

参考:

posted @ 2020-12-10 17:48  gzhjj  阅读(176)  评论(0编辑  收藏  举报