Problem
Given a string s
that contains parentheses and letters, remove the minimum number of invalid parentheses to make the input string valid.
Return all the possible results. You may return the answer in any order.
Note: The input string may contain letters other than the parentheses ( and ).
Examples
Example 1:
Input: s = "()())()"
Output: ["(())()","()()()"]
Example 2:
Input: s = "(a)())()"
Output: ["(a())()","(a)()()"]
Example 3:
Input: s = ")("
Output: [""]
Solution
Method 1 - BFS 🥈
Using BFS, the approach involves generating all possible states by removing one (
or )
at a time. We check if these states are valid, and if valid states are found at the current level, they are added to the final result list, and the search stops. Otherwise, the states are added to the queue and the search continues to the next level.
Using BFS guarantees that the number of parentheses that need to be removed is minimal, also no recursion call is needed in BFS.
Code
Java
class Solution {
public List<String> removeInvalidParentheses(String s) {
List<String> res = new ArrayList<>();
if (s == null) return res;
Set<String> visited = new HashSet<>();
Queue<String> queue = new LinkedList<>();
queue.add(s);
visited.add(s);
boolean found = false;
while (!queue.isEmpty()) {
s = queue.poll();
if (isValid(s)) {
res.add(s);
found = true;
}
if (found) continue;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) != '(' && s.charAt(i) != ')') continue;
String t = s.substring(0, i) + s.substring(i + 1);
if (!visited.contains(t)) {
queue.add(t);
visited.add(t);
}
}
}
return res;
}
private boolean isValid(String s) {
int count = 0;
for (char c : s.toCharArray()) {
if (c == '(') count++;
if (c == ')' && count-- == 0) return false;
}
return count == 0;
}
}
Python
class Solution:
def removeInvalidParentheses(self, s: str) -> List[str]:
res = []
if s is None:
return res
visited = set()
queue = deque([s])
visited.add(s)
found = False
while queue:
s = queue.popleft()
if self.isValid(s):
res.append(s)
found = True
if found:
continue
for i in range(len(s)):
if s[i] != '(' and s[i] != ')':
continue
t = s[:i] + s[i+1:]
if t not in visited:
queue.append(t)
visited.add(t)
return res
def isValid(self, s: str) -> bool:
count = 0
for c in s:
if c == '(':
count += 1
if c == ')':
count -= 1
if count < 0:
return False
return count == 0
Complexity
In BFS, we process the states level by level. In the worst case, we might need to process all levels. Let’s analyze the time complexity level by level.
On the first level, there’s only one string which is the input string s
of length n
. Checking its validity takes O(n)
time. On the second level, we generate C(n, n-1)
new strings by removing one parenthesis from the first level, each of length n-1
. Validating each string takes O(n-1)
time, so total time complexity for this level is (n-1) * C(n, n-1)
. For the third level, it’s (n-2) * C(n, n-2)
. Continuing this pattern, we get:
T(n) = n X C(n, n) + (n-1) X C(n, n-1) + ... + 1 X C(n, 1) = n X 2^{(n-1)}
Method 2 - Backtracking + DFS
Another approach we can think of is following:
- Counting the number of invalid characters that need to be removed from the string to make it valid.
- Using DFS/backtracking to generate all possible substrings by removing the invalid characters, similar to the BFS solution, and exploring each level until all necessary characters have been removed.
- We can create a map of
<String, Boolean>
for memoization to store whether a string has already been processed and if it is valid.
Code
Java
class Solution {
public List<String> removeInvalidParentheses(String s) {
List<String> ans = new ArrayList<>();
Set<String> set = new HashSet<>();
int minBracket = getInvalidBracketCount(s);
helper(s, minBracket, set, ans);
return ans;
}
public void helper(String s, int minBracket, Set<String> set, List<String> ans) {
if (set.contains(s)) return;
set.add(s);
if (minBracket == 0) {
int remove = getInvalidBracketCount(s);
if (remove == 0) {
ans.add(s);
}
return;
}
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) != '(' && s.charAt(i) != ')') {
continue;
}
String t = s.substring(0, i) + s.substring(i + 1);
helper(t, minBracket - 1, set, ans);
}
}
public int getInvalidBracketCount(String s) {
Stack<Character> stack = new Stack<>();
for (char c : s.toCharArray()) {
if (c == '(') {
stack.push(c);
} else if (c == ')') {
if (!stack.isEmpty() && stack.peek() == '(') {
stack.pop();
} else {
stack.push(c);
}
}
}
return stack.size();
}
}
Python
class Solution:
def removeInvalidParentheses(self, s: str) -> List[str]:
ans = []
visited = set()
min_bracket = self.get_invalid_bracket_count(s)
self.helper(s, min_bracket, visited, ans)
return ans
def helper(self, s: str, min_bracket: int, visited: Set[str], ans: List[str]) -> None:
if s in visited:
return
visited.add(s)
if min_bracket == 0:
if self.get_invalid_bracket_count(s) == 0:
ans.append(s)
return
for i in range(len(s)):
if s[i] not in ('(', ')'):
continue
t = s[:i] + s[i+1:]
self.helper(t, min_bracket - 1, visited, ans)
def get_invalid_bracket_count(self, s: str) -> int:
stack = []
for c in s:
if c == '(':
stack.append(c)
elif c == ')':
if stack and stack[-1] == '(':
stack.pop()
else:
stack.append(c)
return len(stack)
Complexity
- ⏰ Time complexity:
O(n * 2^n)
, wheren
is the length of the string. In the worst case, we may need to explore all possible states by removing invalid parentheses one by one. The time complexity is exponential and can be approximated asO(2^n)
- 🧺 Space complexity:
O(2^n)
due to the storage of all possible states in the visited set and the recursive call stack.