Description
Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results.
Note: The input string may contain letters other than the parentheses (
and )
.
Examples:
"()())()" -> ["()()()", "(())()"]
"(a)())()" -> ["(a)()()", "(a())()"]
")(" -> [""]
Credits:
Special thanks to @hpplayer for adding this problem and creating all test cases.
Solution
DFS
很迷的一个写法。。
Key Points:
- Generate unique answer once and only once, do not rely on Set.
- Do not need preprocess.
- Runtime 3 ms.
Explanation:
We all know how to check a string of parentheses is valid using a stack. Or even simpler use a counter
.
The counter will increase when it is ‘(‘ and decrease when it is ‘)’. Whenever the counter is negative, we have more ‘)’ than ‘(‘ in the prefix.
To make the prefix valid, we need to remove a ‘)’. The problem is: which one? The answer is any one in the prefix.
However, if we remove any one, we will generate duplicate results, for example: s = ()), we can remove s[1] or s[2] but the result is the same (). Thus, we restrict ourself to remove the first ) in a series of concecutive )s.
After the removal, the prefix is then valid. We then call the function recursively to solve the rest of the string. However, we need to keep another information: the last removal position. If we do not have this position, we will generate duplicate by removing two ‘)’ in two steps only with a different order.
For this, we keep tracking the last removal position and only remove ‘)’ after that.
Now one may ask. What about ‘(‘?
What if s = ‘(()(()’ in which we need remove ‘(‘?
The answer is: do the same from right to left.
However a cleverer idea is: reverse the string and reuse the code!
Here is the final implement in Java.
class Solution {
public List removeInvalidParentheses(String s) {
List res = new ArrayList<>();
removeRecur(s, 0, 0, res, new char[] {'(', ')'});
return res;
}
public void removeRecur(String s, int start, int removeStart
, List res, char[] par) {
for (int i = start, count = 0; i < s.length(); ++i) {
if (s.charAt(i) == par[0]) {
++count;
} else if (s.charAt(i) == par[1]) {
--count;
}
if (count >= 0) {
continue;
}
for (int j = removeStart; j <= i; ++j) {
if (s.charAt(j) == par[1] && (j == removeStart || s.charAt(j - 1) != par[1])) {
removeRecur(s.substring(0, j) + s.substring(j + 1), i, j, res, par);
}
}
return;
}
String reversed = new StringBuilder(s).reverse().toString();
if (par[0] == '(') { // reverse str not checked yet
removeRecur(reversed, 0, 0, res, new char[] {')', '('});
} else { // reverse str checked
res.add(reversed); // the reverse of reverse str is str
}
}
}
BFS
BFS更好理解一些,虽然比DFS慢很多(140 ms vs 3 ms)。思路类似"Word Ladder"。
可以将原始的str看做一棵树的root节点,它的子节点们是str移除掉一个字符所组成的字符串们,这样当探索到一层节点时,如果遇到一个字符串是左右括号合法的,那么我们就找到了一个所求解,将此层中剩余节点中合法的加到结果集中就行了。
注意为了避免重复,可以用Set
class Solution {
public List removeInvalidParentheses(String s) {
List res = new ArrayList<>();
Queue queue = new LinkedList<>();
Set visited = new HashSet<>();
queue.offer(s);
visited.add(s);
boolean found = false; // used to terminate loop
while (!queue.isEmpty()) {
String curr = queue.poll();
if (isValid(curr)) {
res.add(curr);
found = true;
}
if (found) { // shortest answer found, no need to explore next level
continue;
}
for (int i = 0; i < curr.length(); ++i) {
if (curr.charAt(i) != '(' && curr.charAt(i) != ')') {
continue;
}
String next = curr.substring(0, i) + curr.substring(i + 1);
if (visited.add(next)) {
queue.offer(next);
}
}
}
return res;
}
private boolean isValid(String s) {
int count = 0;
for (int i = 0; i < s.length(); ++i) {
if (s.charAt(i) == '(') {
++count;
} else if (s.charAt(i) == ')') {
--count;
}
if (count < 0) {
return false;
}
}
return count == 0;
}
}