给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。
返回所有可能的结果。答案可以按 任意顺序 返回。
示例 1:
输入:s = "()())()"
输出:["(())()","()()()"]
示例 2:
输入:s = "(a)())()"
输出:["(a())()","(a)()()"]
示例 3:
输入:s = ")("
输出:[""]
题解:力扣
1.回溯加剪枝。
class Solution {
List<String> list = new ArrayList<>();
public List<String> removeInvalidParentheses(String s) {
// 统计需要移除的左括号数或右括号数
int leftRemove = 0;
int rightRemove = 0;
for (int i = 0;i < s.length();i++) {
char cur = s.charAt(i);
if (cur == '(') { // 遇到左括号,就给左括号需要移除的数量加1
leftRemove++;
} else if (cur == ')') { // 遇到右括号
if (leftRemove > 0) { // 如果此时左括号需要移除的数量大于0,就用现在遍历到的右括号抵消左括号
leftRemove--;
} else { // 如果此时左括号需要移除的数量等于0,就说明现在遍历到的右括号是需要移除的
rightRemove++;
}
}
}
helper(s,0,0,0,leftRemove,rightRemove);
return list;
}
public void helper(String s,int index,int leftCount,int rightCount,int leftRemove,int rightRemove) {
if (leftRemove == 0 && rightRemove == 0) { //此时需要移除的数量都移除完了
if (isValid(s)) { //如果移除后的字符串是有效的,就加入list中
list.add(s);
}
return;
}
for (int i = index;i < s.length();i++) {
char cur = s.charAt(i);
if (i == index || cur != s.charAt(i-1)) { // 这里如果相邻的相等那么删除哪个都是一样的,避免重复
if (leftRemove + rightRemove > s.length() - i) { // 剩余的括号数已经不够减了,直接返回
return;
}
if (leftRemove > 0 && cur == '(') { // 如果当前值是左括号,并且左括号有需要删除的数量,就把当前遍历到的左括号删除
// 在删除当前左括号后的子串上继续向后删除
helper(s.substring(0,i) + s.substring(i + 1),i,leftCount,rightCount,leftRemove - 1,rightRemove);
}
if (rightRemove > 0 && cur == ')') {
helper(s.substring(0,i) + s.substring(i + 1),i,leftCount,rightCount,leftRemove,rightRemove - 1);
}
}
// 遇到连续相同的括号,更新左右括号出现的次数
if (cur == '(') {
leftCount++;
} else if (cur == ')') {
rightCount++;
}
if (rightCount > leftCount) { // 如果右括号出现次数大于左括号出现次数了,说明配对出错了,直接退出
break;
}
}
}
//判断当前字符串是否有效
//从左往右遍历如果右括号出现次数大于左括号出现次数,那么就是无效的组合
public boolean isValid(String s) {
int leftRightCount = 0;
for (int i = 0;i < s.length();i++) {
if (s.charAt(i) == '(') { // 遇到左括号,加1
leftRightCount++;
} else if (s.charAt(i) == ')') { // 遇到右括号,减1
leftRightCount--;
if (leftRightCount < 0) { // 如果右括号出现次数大于左括号出现次数,直接返回false
return false;
}
}
}
return leftRightCount == 0;
}
}
2.BFS。在进行广度优先搜索时每一轮删除字符串中的1个括号,直到出现合法匹配的字符串为止,此时进行轮转的次数即为最少需要删除括号的个数。进行广度优先搜索时,每次保存上一轮搜索的结果,然后对上一轮已经保存的结果中的每一个字符串尝试所有可能的删除一个括号的方法,然后将保存的结果进行下一轮搜索。在保存结果时,我们可以利用哈希表对上一轮生成的结果去重,从而提高效率。
class Solution {
public List<String> removeInvalidParentheses(String s) {
List<String> list = new ArrayList<>();
Set<String> currentSet = new HashSet<>();
currentSet.add(s);
while (true) {
// 每一次删除一个字符后都判断是否有合法的,如果有,就是删除最少括号的,直接返回此时的list
for (String str : currentSet) {
if (isValid(str)) {
list.add(str);
}
}
if (list.size() > 0) {
return list;
}
Set<String> nextSet = new HashSet<>();
for (String str : currentSet) {
for (int i = 0;i < str.length();i++) {
if (i > 0 && str.charAt(i) == str.charAt(i - 1)) { // 连续出现一样的括号,选择其中一种情况即可,避免重复
continue;
}
if (str.charAt(i) == '(' || str.charAt(i) == ')') { // 遇到不重复的就删除,然后加入set中
nextSet.add(str.substring(0,i) + str.substring(i + 1));
}
}
}
currentSet = nextSet;
}
}
//判断当前字符串是否有效
//从左往右遍历如果右括号出现次数大于左括号出现次数,那么就是无效的组合
public boolean isValid(String s) {
int leftRightCount = 0;
for (int i = 0;i < s.length();i++) {
if (s.charAt(i) == '(') { // 遇到左括号,加1
leftRightCount++;
} else if (s.charAt(i) == ')') { // 遇到右括号,减1
leftRightCount--;
if (leftRightCount < 0) { // 如果右括号出现次数大于左括号出现次数,直接返回false
return false;
}
}
}
return leftRightCount == 0;
}
}
题源:力扣