class Solution {
// 用集合存储所有正确的字符串,可避免重复
private Set<String> set = new HashSet<>();
public List<String> removeInvalidParentheses(String s) {
char[] ss = s.toCharArray();
int open = 0, close = 0;
// 获取应该去除的左右括号数,手动尝试看
我们从左边开始,一次处理一个括号的表达式。
假设我们遇到一个左括号,即 ( ,它可能会导致或可能不会导致一个无效的表达式,因为表达式的其余部分的某个地方可能有一个匹配的右括号。这里,我们只需增加计数器来跟踪左括号的出现次数。left += 1
如果我们遇到一个右括号,这有两个含义:
要么是这个右括号没有匹配的左括号,在这种情况下,该表达式是无效的。当 left == 0 时,即没有不匹配的左括号可用时,就是这种情况。在这种情况下,我们增加另一个计数器叫 right+=1来表示放错了位置的右括号。
或者,我们有一些未匹配的左括号来匹配这个右括号。当 left > 0 时就是这样。在这种情况下,我们只需减小我们使用的 left 计数器,即 left -= 1。
for (char c : ss) {
if (c == '(') open ++;
else if (c == ')')
if (open > 0) open --;
else close ++;
}
// 回溯
backTracking(ss, new StringBuilder(), 0, 0, 0, open, close);
return new ArrayList(set);
}
public void backTracking(char[] ss, StringBuilder sb, int index, int open, int close, int openRem, int closeRem) {
/**
* 回溯函数
* 分别对字符串中的每一位置的字符进行处理,最终获得符合要求的字符串加入集合中
* @param ss 字符串对应的字符数组
* @param sb 储存当前处理过且未去除字符的字符串
* @param index 当前处理的字符位置
* @param open 当前sb中储存的左括号数
* @param close 当前sb中储存的右括号数
* @param openRem 当前需要去除的左括号数
* @param closeRem 当前需要去除的右括号数
*/
// 所有字符都处理完毕
if (index == ss.length) {
// 如果应去除的左右括号数都变为0,则将sb插入set
if (openRem == 0 && closeRem == 0)
set.add(sb.toString());
return;
}
// 去掉当前位置的字符(括号),并处理下一个字符
if (ss[index] == '(' && openRem > 0 || ss[index] == ')' && closeRem > 0)
backTracking(ss, sb, index + 1, open, close, openRem - (ss[index] == '(' ? 1 : 0), closeRem - (ss[index] == ')' ? 1 : 0));
// 不去掉当前位置字符
// 将当前位置字符插入sb
sb.append(ss[index]);
// 当前位置不为括号,则直接处理下一个字符
if (ss[index] != '(' && ss[index] != ')')
backTracking(ss, sb, index + 1, open, close, openRem, closeRem);
// 当前位置为左括号,增加左括号计数,处理下一个字符
else if (ss[index] == '(')
backTracking(ss, sb, index + 1, open + 1, close, openRem, closeRem);
// 当前位置为右括号,且当前左括号计数大于右括号计数,则增加右括号计数,处理下一个字符
else if (open > close)
backTracking(ss, sb, index + 1, open, close + 1, openRem, closeRem);
// 去除当前加入sb的字符
sb.deleteCharAt(sb.length() - 1);
}
}