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.
classSolution:
defremoveInvalidParentheses(self, s: str) -> List[str]:
res = []
if s isNone:
return res
visited = set()
queue = deque([s])
visited.add(s)
found =Falsewhile queue:
s = queue.popleft()
if self.isValid(s):
res.append(s)
found =Trueif found:
continuefor i in range(len(s)):
if s[i] !='('and s[i] !=')':
continue t = s[:i] + s[i+1:]
if t notin visited:
queue.append(t)
visited.add(t)
return res
defisValid(self, s: str) -> bool:
count =0for c in s:
if c =='(':
count +=1if c ==')':
count -=1if count <0:
returnFalsereturn count ==0
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:
1
T(n)= n X C(n, n)+(n-1) X C(n, n-1)+...+1 X C(n,1)= n X 2^{(n-1)}
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.
classSolution:
defremoveInvalidParentheses(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
defhelper(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)
returnfor i in range(len(s)):
if s[i] notin ('(', ')'):
continue t = s[:i] + s[i+1:]
self.helper(t, min_bracket -1, visited, ans)
defget_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)
⏰ Time complexity: O(n * 2^n), where n 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 as O(2^n)
🧺 Space complexity: O(2^n) due to the storage of all possible states in the visited set and the recursive call stack.