力扣301:删除无效的括号
题目
给你一个由若干括号和字母组成的字符串 s ,删除最小数量的无效括号,使得输入的字符串有效。
返回所有可能的结果。答案可以按 任意顺序 返回。
思路
看到任意顺序所以先想回溯。
想到回溯先想回溯的结束条件是什么:字符串是否是有效的也就是字符串里的括号能否全部匹配成功。所以我们需要设计一个返回值为bool类型的函数判断字符串是否有效,具体实现也好想既然是判断括号是否能否匹配上我们只需要定义一个整型total,遇到左括号就++遇到右括号就--,然后再判断total是否小于0如果小于就说明右括号的数量大于左括号了直接返回false。等循环结束后再判断total是否为0为0就返回true不为零就返回false。
回溯的结束条件讲完了接下来就是如何进行回溯,我们先仔细观察题目。题目上说要删除最小的无效括号所以我们没法随意的删除我们必须先计算出要删除多少个左括号和右括号才能在数量上匹配也就是左括号和右括号的数量是相同的。怎么计算呢?我们定义两个遍历一个左括号的数量leftcount一个右括号的数量rightcount,然后我们遍历字符串遇到左括号就对leftcount++,遇到右括号的时候我们先判断左括号的数量leftcount是否大于零如果大于零说明这一对括号可以匹配上所以要对leftcount进行--。如果小于0说明这个右括号是多余的也就要对rightcount进行++。在一轮遍历后我们就得到了要删除的左括号和右括号数量。在得到了要删除的左括号和右括号的数量后我们就可以开始回溯了,回溯的本体也很简单只要遇到左括号并且leftcount大于0我们就尝试删除左括号,右括号同理。
在回溯的大概流程讲完后就是其中的一些特殊情况,第一回溯结束的判断条件不止是字符串是否有效还有一个条件就是剩下的字符串数量要大于要删除的括号数量也就是leftcount+rightcount。这个道理很简单剩下的字符串全删了都没法完成左右括号数量上的匹配那就可以直接不用玩了。还有一个细节是剪枝方面的,我们要返回的是所以可能的结果所以可能有重复的情况出现,那么什么情况下会有重复呢?多个相同的括号堆在一起也就是连续好几个左括号或者右括号所以我们就需要进行剪枝也就是判断当前位置的符号是否和上一个位置的符号相同,如果相同就直接下一轮。这样就避免了重复答案的出现。
代码
cpp
class Solution {
public:
vector<string> res;
bool IsVaild(string tmp) {
int total = 0;
for (auto ch : tmp) {
if (ch == '(') {
total++;
} else if (ch == ')') {
total--;
}
if (total < 0) {
return false;
}
}
return total == 0;
}
void dfs(string s, int start, int leftcount, int rightcount) {
// 回溯的结束条件:字符串是否是有效的
if (IsVaild(s)) {
res.push_back(s);
return;
}
for (int i = start; i < s.size(); i++) {
// 如果要删除的字符串数量已经大于剩下的字符数就直接返回
if (leftcount + rightcount > s.size() - i) {
return;
}
// 进行剪枝:去除那些连续的括号避免生成重复的字符串
if (i != start && s[i] == s[i - 1]) {
continue;
}
// 去除一个左括号
if (leftcount > 0 && s[i] == '(') {
dfs(s.substr(0, i) + s.substr(i + 1), i, leftcount - 1,
rightcount);
}
// 去除一个右括号
if (rightcount > 0 && s[i] == ')') {
dfs(s.substr(0, i) + s.substr(i + 1), i, leftcount,
rightcount - 1);
}
}
}
vector<string> removeInvalidParentheses(string s) {
// 需要删除的左括号和右括号数量
int leftcount = 0;
int rightcount = 0;
// 先计算出需要删减出多少个括号
// 才能在数量上完成匹配
for (auto ch : s) {
if (ch == '(') {
leftcount++;
} else if (ch == ')') {
// 如果还有左括号那么这两个括号就可以进行匹配也就不用删除了
if (leftcount > 0) {
leftcount--;
} else {
rightcount++;
}
}
}
// 然后通过回溯来完成删除工作
dfs(s, 0, leftcount, rightcount);
return res;
}
};