删除无效的括号 - 深度优先搜索的应用
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
加1,接着看最后一个字符,如果最后一个字符是右括号,rightCount
加1,这时递归到最后一个字符,且左右括号数相等,记录此时删除括号的数量minimumRemoved
以及对应的有效括号validExpressions
。
通过深度优先搜索遍历完所有可能的情况,得到minimumRemoved
的全局最小值。注意到validExpressions
是一个集合,表示可能存在删除括号数相同的多种情况,即删除括号数相同,得到不同的有效括号,这些有效括号都加到集合validExpressions
中。
参考: