Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

301. Remove Invalid Parentheses - Leetcode Solution

Code Implementation

from collections import deque

class Solution:
    def removeInvalidParentheses(self, s: str):
        res = []
        visited = set()
        queue = deque([s])
        found = False

        while queue:
            curr = queue.popleft()
            if self.isValid(curr):
                res.append(curr)
                found = True
            if found:
                continue
            for i in range(len(curr)):
                if curr[i] not in ('(', ')'):
                    continue
                next_str = curr[:i] + curr[i+1:]
                if next_str not in visited:
                    visited.add(next_str)
                    queue.append(next_str)
        return res

    def isValid(self, s):
        count = 0
        for c in s:
            if c == '(':
                count += 1
            elif c == ')':
                count -= 1
                if count < 0:
                    return False
        return count == 0
      
#include <vector>
#include <string>
#include <unordered_set>
#include <queue>

using namespace std;

class Solution {
public:
    vector<string> removeInvalidParentheses(string s) {
        vector<string> res;
        unordered_set<string> visited;
        queue<string> q;
        q.push(s);
        visited.insert(s);
        bool found = false;

        while (!q.empty()) {
            string curr = q.front();
            q.pop();
            if (isValid(curr)) {
                res.push_back(curr);
                found = true;
            }
            if (found) continue;
            for (int i = 0; i < curr.size(); ++i) {
                if (curr[i] != '(' && curr[i] != ')') continue;
                string next = curr.substr(0, i) + curr.substr(i+1);
                if (visited.find(next) == visited.end()) {
                    visited.insert(next);
                    q.push(next);
                }
            }
        }
        return res;
    }

    bool isValid(string s) {
        int count = 0;
        for (char c : s) {
            if (c == '(') count++;
            else if (c == ')') {
                count--;
                if (count < 0) return false;
            }
        }
        return count == 0;
    }
};
      
import java.util.*;

class Solution {
    public List<String> removeInvalidParentheses(String s) {
        List<String> res = new ArrayList<>();
        Set<String> visited = new HashSet<>();
        Queue<String> queue = new LinkedList<>();
        queue.offer(s);
        visited.add(s);
        boolean found = false;

        while (!queue.isEmpty()) {
            String curr = queue.poll();
            if (isValid(curr)) {
                res.add(curr);
                found = true;
            }
            if (found) 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.contains(next)) {
                    visited.add(next);
                    queue.offer(next);
                }
            }
        }
        return res;
    }

    private boolean isValid(String s) {
        int count = 0;
        for (char c : s.toCharArray()) {
            if (c == '(') count++;
            else if (c == ')') {
                count--;
                if (count < 0) return false;
            }
        }
        return count == 0;
    }
}
      
/**
 * @param {string} s
 * @return {string[]}
 */
var removeInvalidParentheses = function(s) {
    let res = [];
    let visited = new Set();
    let queue = [s];
    let found = false;
    while (queue.length) {
        let curr = queue.shift();
        if (isValid(curr)) {
            res.push(curr);
            found = true;
        }
        if (found) continue;
        for (let i = 0; i < curr.length; i++) {
            if (curr[i] !== '(' && curr[i] !== ')') continue;
            let next = curr.slice(0, i) + curr.slice(i+1);
            if (!visited.has(next)) {
                visited.add(next);
                queue.push(next);
            }
        }
    }
    return res;
};

function isValid(s) {
    let count = 0;
    for (let c of s) {
        if (c === '(') count++;
        else if (c === ')') {
            count--;
            if (count < 0) return false;
        }
    }
    return count === 0;
}
      

Problem Description

Given a string s that contains parentheses '(' and ')' as well as other characters, your task is to remove the minimum number of invalid parentheses in order to make the input string valid. You must return all possible results (not just one), and you may return the answers in any order.

  • A string is considered valid if every opening parenthesis '(' has a corresponding closing parenthesis ')' and vice versa, with proper nesting.
  • You must not reuse elements; each removal is a single character deletion.
  • The result should not contain duplicate strings.

Example:
Input: s = "()())()"
Output: ["(())()", "()()()"]

Thought Process

At first glance, you might think about brute-forcing every possible way to remove parentheses and then checking which results are valid. However, this approach is not practical because the number of possible strings grows exponentially as you remove characters.

To optimize, we want to remove the minimum number of parentheses to make the string valid. This means that as soon as we find valid strings with a certain number of removals, we don't need to check strings with more removals.

A good analogy is searching for the shortest path in a maze: once you find a way out with the least steps, you don't need to keep looking for longer paths.

This suggests using Breadth-First Search (BFS) to explore all possible strings by removing one parenthesis at a time, level by level. The first valid strings found are guaranteed to have the minimal number of removals.

Solution Approach

  • 1. Use BFS Traversal:
    • Start with the original string in a queue.
    • At each step, remove one parenthesis (either '(' or ')') from the current string to generate all possible new strings.
    • For each generated string, add it to the queue if it hasn't been seen before.
  • 2. Validity Check:
    • For every string dequeued, check if it is valid (parentheses are balanced).
    • If a valid string is found, add it to the result list.
    • Once any valid string is found at the current BFS level, stop processing further levels (since deeper levels would require more removals).
  • 3. Use a Visited Set:
    • Keep a set of strings that have already been processed to avoid duplicate work.
  • 4. Helper Function for Validity:
    • Implement a function to check if a string has valid parentheses by using a counter (increment for '(', decrement for ')', and ensure it never goes negative).
  • 5. Return All Valid Strings:
    • Collect all valid strings found at the minimum removal level and return them as the answer.

Example Walkthrough

Let's walk through the input s = "()())()":

  1. Level 0: ()())()
    • Not valid (one extra ')')
  2. Level 1 (remove one parenthesis):
    • Remove 1st '(': )())()
    • Remove 2nd ')': ()()()()
    • Remove 3rd ')': ()())()
    • Remove 4th ')': ()()))()
    • Remove 5th '(': ()())()
    • Remove 6th ')': ()())(

    After removing each, we check validity:

    • (())() and ()()() are valid at this level.
  3. Level 2: (would remove two parentheses, but since we've already found valid strings at Level 1, we stop here)

So, the result is ["(())()", "()()()"].

Time and Space Complexity

  • Brute-force Approach:
    • Would generate all possible substrings by removing any combination of parentheses, leading to O(2^n) time and space, where n is the length of s.
  • BFS Approach (Optimized):
    • Still potentially explores up to 2^n substrings in the worst case, but stops as soon as valid strings are found at the minimum removal level.
    • On average, much faster than brute-force because it prunes unnecessary deeper levels.
    • Space complexity is also O(2^n) due to the queue and visited set, but only for distinct substrings generated.

Summary

The key to solving "Remove Invalid Parentheses" efficiently is to use Breadth-First Search (BFS) to explore all possible strings by removing one parenthesis at a time, and to stop as soon as the first valid results are found. This ensures that only the minimal number of removals are performed. Using a visited set prevents redundant work, and a helper function efficiently checks the validity of each string. This approach elegantly balances completeness (finding all solutions) with efficiency, making it both practical and robust.