1.题目描述
给你一个由若干括号和字母组成的字符串 s
,删除最小数量的无效括号,使得输入的字符串有效。
返回所有可能的结果。答案可以按 任意顺序 返回。
示例1:
输入:s = "()())()" 输出:["(())()","()()()"]
示例2:
输入:s = ")(" 输出:[""]
链接:https://leetcode-cn.com/problems/remove-invalid-parentheses/
来源:力扣(LeetCode)
2.思路分析
这道题有DFS和BFS两种解法,因为求的是最短删除数,即最短路径,因此个人推荐优先使用BFS
2.1 深度优先搜索
首先我们利用括号匹配的规则求出该字符串 s 中最少需要去掉的左括号的数目 leftRemove 和右括号的数目 rightRemove,然后我们尝试在原字符串 s 中去掉 leftRemove 个左括号和 rightRemove个右括号,然后检测剩余的字符串是否合法匹配,如果合法匹配则我们则认为该字符串为可能的结果,我们利用回溯算法来尝试搜索所有可能的去除括号的方案。
2.2 广度优先搜索
注意到题目中要求最少删除,这样的描述正是广度优先搜索算法应用的场景,并且题目也要求我们输出所有的结果。我们在进行广度优先搜索时每一轮删除字符串中的 1 个括号,直到出现合法匹配的字符串为止,此时进行轮转的次数即为最少需要删除括号的个数。
我们进行广度优先搜索时,每次保存上一轮搜索的结果,然后对上一轮已经保存的结果中的每一个字符串尝试所有可能的删除一个括号的方法,然后将保存的结果进行下一轮搜索。在保存结果时,我们可以利用哈希表对上一轮生成的结果去重,从而提高效率。
3.解答
3.1 DFS解法
class Solution {
//回溯
//存储返回结果
List<String> res = new ArrayList<>();
public List<String> removeInvalidParentheses(String s) {
//统计需要删除的左右括号数
int leftRemove = 0, rightRemove = 0;
for(int i = 0; i < s.length(); i++){
if(s.charAt(i) == '('){
leftRemove++;
}else if(s.charAt(i) == ')'){
if(leftRemove == 0){
rightRemove++;
}else{
leftRemove--;
}
}
}
//调用回溯方法
helper(s, 0, leftRemove, rightRemove);
if(res.size() == 0){
res.add("");
}
return res;
}
//回溯方法
public void helper(String str, int start, int leftRemove, int rightRemove){
if(leftRemove == 0 && rightRemove == 0){
if(isValid(str)){
res.add(str);
}
return;
}
//遍历
for(int i = start; i < str.length(); i++){
//剪枝
if(i != start && str.charAt(i) == str.charAt(i - 1)){
//跳过重复字符
continue;
}
if(leftRemove + rightRemove > str.length() - 1){
//剩下的字符不可能匹配成功
return;
}
//尝试去掉一个左括号
if(leftRemove > 0 && str.charAt(i) == '('){
helper(str.substring(0, i) + str.substring(i + 1), i, leftRemove - 1, rightRemove);
}
//尝试去掉一个右括号
if(rightRemove > 0 && str.charAt(i) == ')'){
helper(str.substring(0, i) + str.substring(i + 1), i, leftRemove, rightRemove - 1);
}
}
}
//判断括号是否有效
public boolean isValid(String str){
//判断规则:在任何一个位置,左括号的数量应该大于等于右括号的数量
int count = 0;
for(int i = 0; i < str.length(); i++){
if(str.charAt(i) == '('){
count++;
}else if(str.charAt(i) == ')'){
count--;
if(count < 0){
return false;
}
}
}
return count == 0;
}
}
复杂度分析
时间复杂度:O(n×2^n)
空间复杂度:O(n^2)
3.2 BFS解法
class Solution {
//BFS
public List<String> removeInvalidParentheses(String s) {
//存储返回结果
List<String> res = new ArrayList<>();
//存储每一轮字符串的集合
HashSet<String> currSet = new HashSet<>();
currSet.add(s);
//bfs一层一层往外搜索
while(true){
//存储所有符合要求的字符串
for(String str : currSet){
if(isValid(str)){
res.add(str);
}
}
//找到最少删除数就返回
if(res.size() > 0){
return res;
}
//遍历尝试每一种情况
HashSet<String> nextSet = new HashSet<>();
for(String str : currSet){
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) == ')'){
nextSet.add(str.substring(0, i) + str.substring(i + 1));
}
}
}
currSet = nextSet;
}
}
//判断括号是否有效
public boolean isValid(String str){
//判断规则:在任何一个位置,左括号的数量应该大于等于右括号的数量
int count = 0;
for(int i = 0; i < str.length(); i++){
if(str.charAt(i) == '('){
count++;
}else if(str.charAt(i) == ')'){
count--;
if(count < 0){
return false;
}
}
}
return count == 0;
}
}
复杂度分析
时间复杂度:O(n×2^n)。
空间复杂度:O(n×C(n. n/2)。