|
| 1 | +# Remove Invalid Parentheses |
| 2 | + |
| 3 | +You are given a string, `s`, that contains: |
| 4 | +- Lowercase English letters |
| 5 | +- Opening '(' and closing ')' parentheses |
| 6 | + |
| 7 | +A string is considered valid if: |
| 8 | + |
| 9 | +1. All opening parentheses '(' are closed properly by a matching ')'. |
| 10 | +2. The parentheses are in the correct order and nesting. |
| 11 | +3. Letters can appear anywhere and do not affect validity. |
| 12 | + |
| 13 | +Return all possible valid strings that can be formed by removing the minimum number of invalid parentheses. The answer |
| 14 | +must be a list of unique strings, in any order. |
| 15 | + |
| 16 | +**Constraints** |
| 17 | + |
| 18 | +- 1 ≤ `s.length` ≤ 25 |
| 19 | +- `s` consists of lowercase English letters and parentheses `'('` and `')'`. |
| 20 | +- There will be at most `20` parentheses in `s`. |
| 21 | + |
| 22 | +## Examples |
| 23 | + |
| 24 | + |
| 25 | + |
| 26 | + |
| 27 | + |
| 28 | + |
| 29 | +## Solution |
| 30 | + |
| 31 | +The algorithm aims to generate all possible valid strings by removing the minimum number of invalid parentheses. It |
| 32 | +starts with a preprocessing step to determine how many opening and closing parentheses need to be removed. As it scans |
| 33 | +the string, it increments a counter for each opening parenthesis. For each closing parenthesis, it tries to match it |
| 34 | +with an opening one. If no match is found, it marks the closing parenthesis as unmatched. This ensures the algorithm |
| 35 | +knows the minimum number of each type of parenthesis to remove. |
| 36 | + |
| 37 | +Once the number of invalid parentheses is known, the algorithm uses recursive backtracking to explore all valid |
| 38 | +combinations. It processes the string one character at a time and considers several choices: |
| 39 | + |
| 40 | +- If the character is an opening parenthesis (, the algorithm considers two choices: skip it to reduce the number of |
| 41 | + unmatched openings, or add it to the expression and increase the open count. |
| 42 | + |
| 43 | +- If it’s a closing parenthesis ), it can be skipped to reduce unmatched closings, or added to the expression—but only |
| 44 | + if more opening parentheses are already added, to keep the expression balanced. |
| 45 | + |
| 46 | +- If it’s a non-parenthesis character, it is always added to the current expression. |
| 47 | + |
| 48 | +The recursion continues until the end of the string is reached. At that point, if the number of opening and closing |
| 49 | +parentheses is equal (i.e., the expression is balanced) and no more removals are needed, the expression is added to a |
| 50 | +result set to ensure uniqueness. After exploring all possibilities, the algorithm returns all unique valid expressions |
| 51 | +as a list. |
| 52 | + |
| 53 | +The steps of the algorithm are as follows: |
| 54 | + |
| 55 | +1. Count the minimum invalid parentheses to remove: |
| 56 | + - Initialize two counters, `left_to_remove` (Number of extra '(' to remove) and `right_to_remove` (Number of extra |
| 57 | + `')'` to remove). |
| 58 | + - Iterate through the string: |
| 59 | + - If the character is `'('`, increment `left_to_remove`. |
| 60 | + - If the character is `')'`: |
| 61 | + - If `left_to_remove` > 0, a matching '(' exists, so decrement `left_to_remove`. |
| 62 | + - Else, increment `right_to_remove` (unmatched right parenthesis). |
| 63 | + |
| 64 | +2. We define a recursive helper function, `backtrack(index, open_count, close_count, path, left_remain, right_remain)` |
| 65 | + to explore all valid combinations of the input string. index is the current position in the string. `open_count` and |
| 66 | + `close_count` track the number of '(' and ')' in the current path to maintain balance. path holds the built string |
| 67 | +so far. `left_remain` and `right_remain` indicate how many `'('` and `')'` can still be removed to form a valid expression. |
| 68 | + |
| 69 | + - When the end of the string is reached (i.e., `index == len(s)`), check if no removals remain (`left_remain == 0` and |
| 70 | + `right_remain == 0`) and the parentheses are balanced (`open_count == close_count`). If so, add the current path to |
| 71 | + the result set. |
| 72 | + - Recursive case: At each character `char = s[index]`: |
| 73 | + - If `char == '('`: |
| 74 | + - Option to remove it (if `left_remain > 0`): |
| 75 | + - Recurse without adding '(', decrementing `left_remain`. |
| 76 | + - Option to keep it: |
| 77 | + - Recurse adding `'('` to path, incrementing `open_count`. |
| 78 | + - If `char == ')'`: |
| 79 | + - Option to remove it (if `right_remain > 0`): |
| 80 | + - Recurse without adding `')'`, decrementing `right_remain`. |
| 81 | + - Option to keep it: |
| 82 | + - Only if `close_count < open_count` (to ensure balance). |
| 83 | + - Recurse adding `')'`, incrementing `close_count`. |
| 84 | + |
| 85 | + - If char is not a parenthesis: |
| 86 | + - Always keep it and recurse with the character added to `path`. |
| 87 | + |
| 88 | +3. Call `backtrack(0, 0, 0, '', left_to_remove, right_to_remove)` to begin from index 0 and an empty path. |
| 89 | +4. Convert the result set `result` to a list and return it. |
| 90 | + |
| 91 | +### Time Complexity |
| 92 | + |
| 93 | +At each step of the algorithm, a parenthesis can either be removed or kept, which results in two choices per parenthesis. |
| 94 | +Therefore, for a string with `n` parentheses, the total number of combinations explored by the backtracking algorithm |
| 95 | +can grow exponentially, leading to a branching factor of `O(2^n)`. Since letters are always included without affecting |
| 96 | +the decision tree, they do not contribute to the branching complexity. As a result, the overall time complexity of the |
| 97 | +algorithm in the worst case is `O(2^n)`. |
| 98 | + |
| 99 | +### Space Complexity |
| 100 | + |
| 101 | +The space complexity of the above solution is `O(n)`, where `n` is the length of the input string because the recursive |
| 102 | +call stack in the backtracking algorithm can reach a depth of `n`. |
0 commit comments