We have a string that contains lowercase letters and parentheses. Some of those parentheses might be unmatched, meaning there's a ( without a corresponding ), or vice versa. We need to remove the minimum number of parentheses to make every remaining parenthesis properly matched.
Lowercase letters never cause problems. They can stay no matter what. The only characters we would ever remove are ( or ), and we want to remove as few as possible.
So the real question is which specific parentheses are unmatched. If we can identify exactly those, we remove them and keep everything else. A closing parenthesis ) is unmatched when no open ( precedes it to pair with, and an opening parenthesis ( is unmatched when it is left over after every ) has been paired.
1 <= s.length <= 10^5 -- With n up to 100,000, an O(n^2) approach risks timing out. We want a linear scan.s[i] is either '(', ')', or lowercase English letter -- Only two characters can ever be removed. Letters are always kept.A stack matches parentheses by tracking which open ( is still waiting for a partner. Scan the string from left to right. When the character is (, push its index onto the stack. When it is ), look at the stack: if it holds an index, pop it (this ) pairs with that (); if the stack is empty, this ) has no ( before it, so it is unmatched and must be removed.
After the scan, any indices still on the stack are ( characters that never found a ), so they are unmatched too.
The stack gives us the indices of every character to remove. We collect them into a set, then build the final string from every character whose index is not in that set.
indicesToRemove.(, push its index onto the stack.):indicesToRemove (unmatched )).indicesToRemove (unmatched ().indicesToRemove.Loading animation...
This works, but the stack and hash set are not strictly necessary. The next approach replaces both with a single counter and two passes over the string.
Unmatched parentheses come in two forms, and each can be caught with a single integer counter instead of a stack:
): A ) is unmatched when there is no preceding unmatched ( to pair with. Scanning left to right, keep a counter of how many ( are open. When the counter is zero and we see a ), it is unmatched.(: A ( is unmatched when no ) follows it before the end of the string. Scanning right to left, keep a counter of how many ) are open. When that counter is zero and we see a (, it is unmatched.So we make two passes. The first pass (left to right) removes every unmatched ). The second pass (right to left) removes every unmatched (. After both passes, every remaining parenthesis is matched. A counter holds a single number per pass, so the only extra space is the output itself.
The counter is a stack with only its height retained. We never need to know which ( a given ) pairs with, only whether one is available, so a count is enough. When openCount is zero and a ) arrives, no open ( exists to its left, so removing that ) is forced, never a choice.
Two directions are required because the two failure modes are not symmetric within one pass. A left-to-right scan cannot tell whether an open ( will eventually be closed, so it cannot safely remove a (. After pass one, every ) is already paired, so the leftover unmatched parentheses are all (, and the right-to-left scan removes exactly those. Each removal is forced, so the total removed is the minimum possible.
).openCount = 0.(, increment openCount.): if openCount > 0, decrement openCount (matched pair). If openCount == 0, skip this character (unmatched )).(.closeCount = 0.), increment closeCount.(: if closeCount > 0, decrement closeCount (matched pair). If closeCount == 0, skip this character (unmatched ().Loading animation...