Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results.
Note: The input string may contain letters other than the parentheses ( and ).
题意:去掉最少的括号,使得表达式的括号匹配,返回所有符合条件的结果。
思路:
- 题目对去掉的括号数有限制,要求尽可能少,因此我们需要统计一下输入的表达式最少需要去掉几个括号后就可以匹配。用ml和mr表示最少需要去掉的左括号数和右括号数,遍历输入遇到(则ml++,遇到),如果ml大于0,ml--,否则mr++。
- 采用深度优先搜索,对于字符串每个位置的字符,如果它是(或),我们都可以选择保留它或者去掉它。当选择保留时,ml或mr保持不变;当选择去掉时,需要将ml或者mr减1,因为我们去掉了一个括号,对应的最小去掉括号计数器也应该相应减少。
- 当搜索到字符串末尾时,如果ml和mr都刚好为0,证明我们去掉的括号数刚刚好,则可以把搜索的结果加入到结果集合中。此时还有一点要注意,我们的括号能匹配吗,因此还需要一个变量open来记录当前括号是否能匹配。
- 再者为了结果集去重,结果集应声明为一个HashSet类型
- 每次搜索后需要setLength,把buffer重置,否则会导致回溯之后的buffer仍带着之前append的字符
public List removeInvalidParentheses(String s) {
int rmL = 0, rmR = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
rmL++;
} else if (s.charAt(i) == ')') {
if (rmL != 0) {
rmL--;
} else {
rmR++;
}
}
}
Set res = new HashSet<>();
dfs(s, 0, res, new StringBuilder(), rmL, rmR, 0);
return new ArrayList(res);
}
public void dfs(String s, int i, Set res, StringBuilder sb, int rmL, int rmR, int open) {
if (rmL < 0 || rmR < 0 || open < 0) {
return;
}
if (i == s.length() && rmL == 0 && rmR == 0 && open == 0) {
res.add(sb.toString());
return;
}
// System.out.println("before:" + sb.toString());
char c = s.charAt(i);
int len = sb.length();
if (c == '(') {
//先not use再use,不能颠倒顺序,否则先append的结果会带到下面导致open计数会不准
dfs(s, i + 1, res, sb, rmL - 1, rmR, open); // not use (
dfs(s, i + 1, res, sb.append(c), rmL, rmR, open + 1); // use (
} else if (c == ')') {
dfs(s, i + 1, res, sb, rmL, rmR - 1, open); // not use )
dfs(s, i + 1, res, sb.append(c), rmL, rmR, open - 1); // use )
} else {
dfs(s, i + 1, res, sb.append(c), rmL, rmR, open);
}
// System.out.println("after:" + sb.toString());
sb.setLength(len);
// System.out.println("set:" + sb.toString());
}