题目地址:
https://leetcode.com/problems/remove-invalid-parentheses/
给定一个含左右括号的字符串 s s s(可能还含别的字符),要求删掉其若干括号使得括号序列是合法的。问最少删掉多少括号,返回删去括号之后的字符串的所有方案。
一个括号序列是合法的,当且仅当:
1、左括号右括号数量相等;
2、任意前缀中,左括号数量大于等于右括号数量。
那么我们可以先扫描一遍
s
s
s,算出至少要删除的左括号和右括号的数量。用l
和r
记录左括号右括号个数。如果遇到左括号,则l++
;如果遇到右括号,则看一下l == r
是否成立,如果成立,说明当前遍历到的右括号一定要删掉,否则可以不删;当遍历结束之后,看一下l > r
是否成立,如果成立,则要将多余的左括号删掉。所以扫描完之后就能知道要删除多少个左右括号了。更简单的办法是用栈模拟,直接就设l
和r
是要删的左右括号个数,想象有一个栈,遇到左括号就入栈(即l++
,l
是栈内左括号的个数),遇到右括号,如果栈里没有左括号,那么该右括号要删掉,从而r++
;如果栈里有左括号,那么左括号出栈(即l--
)。最后的l
就是要删除的左括号个数,r
是要删除的右括号个数。
接下来,就是DFS的过程了。在每个位置,如果当前位置是(
,那么就枚举可以删除多少个左括号;如果当前位置是)
则类似做;如果当前位置不是括号,那就直接append。每层递归的时候都要保证已经append的括号序列是合法前缀,并且左右括号删掉的个数没有超过上面算出来的个数。枚举到字符串末尾时如果得到了合法字符串就加入答案。代码如下:
class Solution {
public:
vector<string> removeInvalidParentheses(string s) {
// 求一下要删除的左右括号的个数
int l = 0, r = 0;
for (int i = 0; i < s.size(); i++) {
if (s[i] == '(')
l++;
else if (s[i] == ')') {
if (!l)
r++;
else
l--;
}
}
vector<string> res;
string t;
dfs(0, s, t, 0, l, r, res);
return res;
}
// u是指当前遍历到了s[u],t是当前生成的字符串,diff是t中左右括号的个数之差,l和r是还需要删除多少个左右括号
void dfs(int u, string& s, string& t, int diff, int l, int r,
vector<string>& res) {
// 如果遍历到末尾了,那么得到了一个方案t,如果t满足合法括号条件,则加入答案。dfs的时候一定保证diff非负
if (u == s.size()) {
if (!diff) res.push_back(t);
return;
}
// 如果当前字符不是括号,则必须加入答案
if (s[u] != '(' && s[u] != ')') {
t += s[u];
dfs(u + 1, s, t, diff, l, r, res);
t.pop_back();
return;
}
// 否则说明是括号
if (s[u] == '(') {
// 数一下有多少个左括号
int i = u;
while (i < s.size() && s[i] == '(') i++;
// 枚举删掉多少个左括号
for (int k = i - u; k >= 0; k--) {
// 如果还能删,就枚举之
if (k <= l) dfs(i, s, t, diff + (i - u - k), l - k, r, res);
t += '(';
}
// 恢复现场
t.erase(t.end() - (i - u + 1), t.end());
} else {
int i = u;
while (i < s.size() && s[i] == ')') i++;
for (int k = i - u; k >= 0; k--) {
// 这里有两个条件,删的右括号数不多于r,并且删掉之后仍然满足t中左括号数>=右括号数
if (k <= r && diff - (i - u - k) >= 0)
dfs(i, s, t, diff - (i - u - k), l, r - k, res);
t += ')';
}
t.erase(t.end() - (i - u + 1), t.end());
}
}
};
时间复杂度 O ( n 2 n ) O(n2^n) O(n2n),空间 O ( n ) O(n) O(n)。