刪除無效的括號 - 深度優先搜尋的應用
阿新 • • 發佈:2020-12-10
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); } }
根據題意,如果當前字元不是左括號(
或者右括號)
,就將其直接新增到解決方案字串中,因為它不影響括號的有效性。
對於左括號和右括號,有兩種情況,即刪除和不刪除。
如果當前字元是左括號或者右括號,就將其刪除,接著看下一個字元。當遞迴刪除到最後一個字元時,然後回溯將最後一個字元新增到解決方案字串中(不刪除)。
這裡用leftCount
和rightCount
記錄考慮的左括號數和右括號數,由於是最後一個字元,且前面的字元被遞迴刪除了,所以不能形成有效的符號。
接著刪除解決方案字串中最後一個字元(一會兒還會遞迴到最後一個字元,但已經是另一個情況了),繼續回溯將倒數第二個字元新增到解決方案字串中。如果倒數第二個字元是左括號,leftCount
rightCount
加1,這時遞迴到最後一個字元,且左右括號數相等,記錄此時刪除括號的數量minimumRemoved
以及對應的有效括號validExpressions
。
通過深度優先搜尋遍歷完所有可能的情況,得到minimumRemoved
的全域性最小值。注意到validExpressions
是一個集合,表示可能存在刪除括號數相同的多種情況,即刪除括號數相同,得到不同的有效括號,這些有效括號都加到集合validExpressions
中。
參考: