Minimum Remove to Make Valid Parentheses

Updated on 17 June, 2025
Minimum Remove to Make Valid Parentheses header image

Problem Statement

In this task, we're working with a string s that comprises parentheses ('(' and ')') as well as lowercase English characters. The objective is to modify the string by removing the least number of parentheses to make the string valid according to the rules of a "valid parentheses string." These rules define that a string is valid if it is either an empty string, a string that only contains lowercase characters, or a string that can be decomposed into subsections that either stand alone as valid strings or are enclosed valid strings within parentheses. The solution should return any version of a valid string that can be achieved through the minimal removal of parentheses.

Examples

Example 1

Input:

s = "val(i(d)e)n)"

Output:

"val(i(d)e)n"

Explanation:

"val(i(de)en)", "val(id(e)n)" would also be accepted.

Example 2

Input:

s = "a)b(c)d"

Output:

"ab(c)d"

Example 3

Input:

s = "))(("

Output:

""

Explanation:

An empty string is also valid.

Constraints

  • 1 <= s.length <= 10⁵
  • s[i] is either '(', ')', or a lowercase English letter.

Approach and Intuition

To solve this problem effectively, we can rely on a stack-based approach combined with two-pass processing of the string:

  1. First Pass: Identify Excessive Closing Brackets

    • Initialize an empty stack and iterate through the string.

    • For each character:

      • If it is '(', push it onto the stack.
      • If it is ')', check if the stack is non-empty and the top is '('. If true, pop the stack (i.e., you found a matching pair); otherwise, mark this ')' as redundant by replacing it with a special character (like '*') in the string.
    • By the end of this pass, all unneeded ')' characters are marked.

  2. Second Pass: Identify Excessive Opening Brackets

    • Clear the stack and traverse the string again, this time from right to left.

    • For each character:

      • If it is ')', push it onto the stack.
      • If it is '(', check if the stack is non-empty and the top is ')'. If true, pop the stack; otherwise, mark this '(' as redundant by replacing it with '*'.
    • Unmatched '(' characters are marked by the end of this pass.

  3. Construct the Valid String

    • Traverse the modified string and build a new string by skipping all '*' characters. This string is now a valid parentheses string.

Key Points to Consider:

  • Each removed character should be counted to ensure that the removal is minimal.
  • The stack helps in tracking unmatched parentheses effectively.
  • The approach is efficient with a linear time complexity O(n) due to the two linear passes over the string. Given that the string manipulation for each character is constant time, this is optimal.

Using the examples given, it's clear how this method systematically identifies and removes the necessary characters to enforce the validity of the string while adhering to the requirement of minimal removal. This method is robust and handles all edge cases such as strings of only parentheses or mixed with letters.

Solutions

  • Java
  • Python
java
class Solution {

    public String makeValid(String inputString) {
        
        // First pass: Remove excess closing parentheses
        StringBuilder tempBuilder = new StringBuilder();
        int countOpen = 0;
        int netBalance = 0;
        for (int index = 0; index < inputString.length(); index++) {
            char character = inputString.charAt(index);
            if (character == '(') {
                countOpen++;
                netBalance++;
            } else if (character == ')') {
                if (netBalance == 0) continue;
                netBalance--;
            }
            tempBuilder.append(character);
        }

        // Second pass: Remove excess opening parentheses
        StringBuilder finalResult = new StringBuilder();
        int keepOpen = countOpen - netBalance;
        for (int index = 0; index < tempBuilder.length(); index++) {
            char character = tempBuilder.charAt(index);
            if (character == '(') {
                keepOpen--;
                if (keepOpen < 0) continue;
            }
            finalResult.append(character);
        }

        return finalResult.toString();
    }
}

The given Java solution addresses the problem of removing the minimum number of parentheses to make a string of parentheses valid. The approach uses a two-pass method to ensure the string is valid without unnecessary parentheses while maintaining the original order of characters.

  • In the first pass:

    • Traverse the input string character by character using a StringBuilder to construct an intermediate result.
    • Maintain a balance (netBalance) of open to close parentheses. When encountering an open parenthesis '(', increase both countOpen and netBalance. For a close parenthesis ')', decrease netBalance only if it's greater than zero, ensuring not to account for unmatched ')' characters.
  • In the second pass:

    • Construct the final string from the intermediate string built in the first pass.
    • Use another StringBuilder, and process each character.
    • Allow only the necessary number of '(' based on the balance left from the first pass (keepOpen calculated as countOpen - netBalance).
    • Append only valid parentheses to the final result, thus ensuring all unbalanced '(' are omitted.

The resulting string returned by the method makeValid is the input string stripped of the minimum number of parentheses required to make its structure valid, fulfilling the problem's requirements efficiently.

python
class Solution:
    def makeValidParentheses(self, input_str: str) -> str:
        # First phase: erase extraneous ")"
        processed_chars = []
        count_balance = 0
        count_open_parens = 0
        for char in input_str:
            if char == "(":
                count_balance += 1
                count_open_parens += 1
            if char == ")":
                if count_balance == 0:
                    continue
                count_balance -= 1
            processed_chars.append(char)

        # Second phase: erase extra "(" starting from the end
        final_result = []
        opens_remaining = count_open_parens - count_balance
        for char in processed_chars:
            if char == "(":
                opens_remaining -= 1
                if opens_remaining < 0:
                    continue
            final_result.append(char)

        return "".join(final_result)

The Python code provided defines a method named makeValidParentheses within the Solution class, aiming to solve the problem of ensuring parentheses in a string are properly balanced. This is done via a two-phase process, designed to first remove extraneous closing parentheses and then to identify and remove surplus opening parentheses. Here's a breakdown of how this solution operates:

  • The function accepts an input_str argument, representing the initial string containing potentially unbalanced parentheses.
  • Initialization: Several variables are set up:
    • processed_chars: a list to hold characters that either pass the validity check or will be further checked in the second phase.
    • count_balance: a counter used to ensure there are no more right parentheses being closed than the left ones opened.
    • count_open_parens: a counter to track the number of opening parentheses.
  • First Phase - Removing Extraneous Closing Parentheses ())
    1. Iterate through each character in the input_str.
    2. If an opening parenthesis is encountered, increment both count_balance and count_open_parens.
    3. If a closing parenthesis is encountered, check whether there is a preceding unmatched opening parenthesis (count_balance is non-zero). If not, this closing parenthesis is skipped.
    4. Add permissible characters to the processed_chars list.
  • Second Phase - Adjusting Unmatched Opening Parentheses (()
    1. Initialize opens_remaining to the difference between total opening parentheses and matched closing ones.
    2. Iterate through the processed_chars.
    3. For each opening parenthesis, decrement opens_remaining. If decrementing results in a negative value, skip adding this character; it means this opening parenthesis doesn't have a matching closing one.
    4. Append all other characters to the final results list.
  • Compile the Result:
    • Convert the list of valid characters (final_result) back into a string and return it.

This approach efficiently resolves the parentheses balancing by minimizing the number of passes over the string and using data structures that facilitate fast updates and checks.

Comments

No comments yet.