Loading problems...
TL;DR: Use a stack to track indices of unmatched open parentheses and a set (or placeholder markers) to identify invalid closing parentheses, then reconstruct the string excluding the identified invalid indices.
The LeetCode 1249 problem, Minimum Remove to Make Valid Parentheses, asks us to sanitize a string containing lowercase letters and parentheses. The goal is to remove the fewest number of parentheses possible so that the remaining string is a valid parentheses string. A string is valid if every opening parenthesis has a corresponding closing parenthesis and they are correctly nested.
This is a popular interview question because it tests the candidate's ability to manipulate strings efficiently while enforcing logical invariants using linear data structures.
The naive approach to solving Minimum Remove to Make Valid Parentheses is to treat it as a subset generation problem. We want to find the longest subsequence of the original string that is valid.
s.Pseudo-code:
function solve(s):
subsequences = generateAllSubsequences(s)
sort(subsequences, key=length, descending)
for sub in subsequences:
if isValid(sub):
return subComplexity Analysis: The number of subsequences for a string of length is . Checking validity takes . The total time complexity is roughly .
Why it fails:
With constraints where s.length can be up to , an exponential solution is impossible. This approach will immediately result in a Time Limit Exceeded (TLE) error. We need a linear time solution.
The core insight for the optimal solution is realizing that we only need to remove a parenthesis if it violates the validity rules immediately or remains unresolved at the end of the string.
We can define invalidity in two specific ways:
): A closing parenthesis is invalid if there is no preceding unmatched opening parenthesis available to pair with it.(: An opening parenthesis is invalid if, after scanning the entire string, it was never closed by a subsequent ).The Stack - Valid Parentheses Matching pattern allows us to enforce this logic in a single pass (or two passes depending on implementation).
Visual Description: Imagine scanning the string from left to right. We maintain a stack of indices.
(, we push its index onto the stack. This signifies an open bracket waiting for a match.), we check the stack:
) has successfully matched with the ( at the popped index.) has no match. We mark this specific index for removal.( characters that were never closed. These must also be marked for removal.By storing indices rather than characters, we gain the ability to pinpoint exactly which characters to delete from the original string.

Let's trace s = "a)b(c)d".
) is invalid. Mark index 1 for removal.3 to stack. Stack: [3].[3]). Match found. Pop 3. Stack: [].(.{1}.
0 ('a')1 (')')2 ('b')3 ('(')4 ('c')5 (')')6 ('d')"ab(c)d".The algorithm guarantees the minimum number of removals based on the definition of valid parentheses.
) is only removed if there is strictly no ( available to match it at that point in the scan. Keeping it would structurally violate validity.( is only removed if there is strictly no ) available to close it after it appears. Keeping it would leave an open group.Since we only remove characters that must be removed to satisfy the validity constraint, the number of removals is minimized, and the resulting string is valid.
cpp#include <string> #include <stack> #include <vector> class Solution { public: string minRemoveToMakeValid(string s) { std::stack<int> st; // Iterate through string to find invalid parentheses for (int i = 0; i < s.length(); ++i) { if (s[i] == '(') { st.push(i); } else if (s[i] == ')') { if (!st.empty()) { st.pop(); // Match found } else { s[i] = '*'; // Mark invalid closing parenthesis } } } // Mark remaining invalid open parentheses while (!st.empty()) { s[st.top()] = '*'; st.pop(); } // Construct the result string std::string result = ""; for (char c : s) { if (c != '*') { result += c; } } return result; } };
javaclass Solution { public String minRemoveToMakeValid(String s) { StringBuilder sb = new StringBuilder(s); Stack<Integer> stack = new Stack<>(); // Pass 1: Remove invalid closing parentheses and track open ones for (int i = 0; i < sb.length(); i++) { char c = sb.charAt(i); if (c == '(') { stack.push(i); } else if (c == ')') { if (!stack.isEmpty()) { stack.pop(); } else { sb.setCharAt(i, '*'); // Mark for deletion } } } // Pass 2: Mark remaining open parentheses while (!stack.isEmpty()) { sb.setCharAt(stack.pop(), '*'); } // Pass 3: Build final string StringBuilder result = new StringBuilder(); for (int i = 0; i < sb.length(); i++) { char c = sb.charAt(i); if (c != '*') { result.append(c); } } return result.toString(); } }
pythonclass Solution: def minRemoveToMakeValid(self, s: str) -> str: s_list = list(s) stack = [] # Pass 1: Identify invalid ')' and track '(' for i, char in enumerate(s): if char == '(': stack.append(i) elif char == ')': if stack: stack.pop() else: s_list[i] = '' # Mark invalid ')' for deletion # Pass 2: Identify invalid '(' remaining in stack while stack: s_list[stack.pop()] = '' return "".join(s_list)
"(((("), the stack stores indices. The set for removal indices and the output string builder also take space.The Stack - Valid Parentheses Matching pattern is fundamental for several LeetCode problems.
Mastering the index-tracking stack technique in LeetCode 1249 provides the tools to solve all of the above efficiently.
Why does the naive solution TLE?
(, decrement for )) instead of a stack of indices.( at the end, a counter tells you "you have 2 extra", but not which indices they occupy.( to remove to satisfy the problem requirements.Why is string concatenation inside the loop bad?
result += char inside the main loop in Java or Python.StringBuilder (Java) or list joining (Python).