题目描述
给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。
返回所有可能的结果。答案可以按 任意顺序 返回。
解题思路
主要是三个部分:
- 判断括号是否有效的函数 judge;
- 找出最大有效括号的左括号数(left)和右括号数(right);
- 以回溯的方式(backtrack)删除多余的括号,并用judge验证所得结果是否有效。
判断括号是否有效
private boolean judge(String s){
Deque<Character> stack = new ArrayDeque<Character>();
char[] chs = s.toCharArray();
for(char ch : chs){
if(ch == '(')
stack.push(ch);
else if(ch == ')') {
if(stack.isEmpty()) return false;
else stack.pop();
}
}
if(!stack.isEmpty()) return false;
return true;
}
最大有效括号的左括号数(left)、右括号数(right)
List<String> list = new ArrayList<String>();
// 初始条件下,括号匹配,直接返回
if(judge(s)) {list.add(s);return list;}
Deque<Character> stack = new ArrayDeque<Character>();
int left = 0, right = 0;
char[] chs = s.toCharArray();
// 类似于判断括号是否匹配:统计左括号的个数,right返回的是可以有效匹配的最大右括号数
for(int i=0; i<chs.length; i++){
if(chs[i] == '(') {stack.push(chs[i]);left++;}
else if(chs[i] == ')') {
right++;
if(stack.isEmpty()) right--;
else stack.pop();
}
}
left -= stack.size(); // 有效匹配的最大左括号的个数
回溯
在上一步中已经获得了最终结果中所获得的左右括号数left、right,只需要删除多余的左右括号,所得结果中的左右括号数就和正确结果中的一致,但应该删除哪些位置中的括号呢?在这里是先得到所有结果,然后再用第一步中的judge去验证,验证通过的即为所求,具体如下:
private void backtrack(int j,int left,int right,char[] chs,StringBuilder sb,Set<String> set){
for(int i=j; i<chs.length; i++){
if(chs[i] == '(')
if(left>0) {
sb.append(chs[i]);
left--;
}
else continue;
else if(chs[i] == ')')
if(right>0) {
sb.append(chs[i]);
right--;
}
else continue;
else sb.append(chs[i]);
backtrack(i+1,left,right,chs,sb,set);
sb.deleteCharAt(sb.length()-1);
if(chs[i] == '(') left++; else right++;
}
if(left == 0 && right == 0) {
System.out.println(sb.toString());
if(judge(sb.toString())) set.add(sb.toString());
}
}
完整代码如下
class Solution {
public List<String> removeInvalidParentheses(String s) {
List<String> list = new ArrayList<String>();
// 初始条件下,括号匹配,直接返回
if(judge(s)) {list.add(s);return list;}
Deque<Character> stack = new ArrayDeque<Character>();
int left = 0, right = 0;
char[] chs = s.toCharArray();
// 类似于判断括号是否匹配:统计左括号的个数,right返回的是可以有效匹配的最大右括号数
for(int i=0; i<chs.length; i++){
if(chs[i] == '(') {stack.push(chs[i]);left++;}
else if(chs[i] == ')') {
right++;
if(stack.isEmpty()) right--;
else stack.pop();
}
}
left -= stack.size(); // 有效匹配的最大左括号的个数
Set<String> set = new HashSet<String>();
backtrack(0,left,right,chs,new StringBuilder(),set);
for(String src : set) list.add(src);
return list;
}
// left,right是有效匹配的最大左右括号数
// 例如,左括号比右括号多一个,就把所有位置的左括号依次删除一个,再将得到的结果进行校验,满足则保存
// 左括号比右括号多二个,就从所有位置的左括号任选两个删除,并依次遍历所有情况,再将得到的结果进行校验,满足则保存
private void backtrack(int j,int left,int right,char[] chs,StringBuilder sb,Set<String> set){
for(int i=j; i<chs.length; i++){
if(chs[i] == '(')
if(left>0) {
sb.append(chs[i]);
left--;
}
else continue;
else if(chs[i] == ')')
if(right>0) {
sb.append(chs[i]);
right--;
}
else continue;
else sb.append(chs[i]);
backtrack(i+1,left,right,chs,sb,set);
sb.deleteCharAt(sb.length()-1);
if(chs[i] == '(') left++; else right++;
}
if(left == 0 && right == 0) {
System.out.println(sb.toString());
if(judge(sb.toString())) set.add(sb.toString());
}
}
// 判断括号是否正确
private boolean judge(String s){
Deque<Character> stack = new ArrayDeque<Character>();
char[] chs = s.toCharArray();
for(char ch : chs){
if(ch == '(') stack.push(ch);
else if(ch == ')') {
if(stack.isEmpty()) return false;
else stack.pop();
}
}
if(!stack.isEmpty()) return false;
return true;
}
}