1. 程式人生 > 實用技巧 >刪除無效的括號 - 深度優先搜尋的應用

刪除無效的括號 - 深度優先搜尋的應用

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

參考: