A Solution to Leetcode 1249. Minimum Remove to Make Valid Parentheses

A Solution to Leetcode 1249. Minimum Remove to Make Valid Parentheses

Problem statement

Given a string s of '(' , ')' and lowercase English characters.

Your task is to remove the minimum number of parentheses ( '(' or ')', in any positions) so that the resulting parentheses string is valid and return any valid string.

Formally, a parentheses string is valid if and only if:

  • It is the empty string, contains only lowercase characters, or
  • It can be written as AB (A concatenated with B), where A and B are valid strings, or
  • It can be written as (A), where A is a valid string.

Example 1

Input: s = "lee(t(c)o)de)"
Output: "lee(t(c)o)de"
Explanation: "lee(t(co)de)" , "lee(t(c)ode)" 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^5.
  • s[i] is either '(' , ')', or lowercase English letters.

Solution: Stack is a friend

  • Construct the result from the valid characters of s.
  • You can use a stack to store the positions of the open brackets '(' which can be canceled out by later closed ones ')'.
  • Any remaining '(' are removed.

Code

#include <iostream>
#include <stack>
using namespace std;
string minRemoveToMakeValid(string s) {
    string result;
    stack<int> pos;
    for (char c: s) {
        if (c == ')' && pos.empty() ) {
            continue;
        }
        result += c;
        if (c == '(') {
            pos.push(result.length() - 1);
        } else if (c == ')') {
            pos.pop();
        }
    }
    while (!pos.empty()) {
        result.erase(result.begin() + pos.top());
        pos.pop();
    }
    return result;
}
int main() {
    cout << minRemoveToMakeValid("lee(t(c)o)de)") << endl;
    cout << minRemoveToMakeValid("a)b(c)d") << endl;
    cout << minRemoveToMakeValid("))((") << endl;
}
Output:
lee(t(c)o)de
ab(c)d

Complexity

  • Runtime: O(N), where N = s.length.
  • Extra space: O(N).

Refactorization code

If there are many string::erase() are performed, the runtime would not be good since the complexity of this function is not constant.

One way to avoid it is constructing the new result from scratch skipping the marked characters.

Code

#include <iostream>
#include <stack>
using namespace std;
string minRemoveToMakeValid(string s) {
    {
        stack<int> pos;
        for (int i = 0; i < s.length(); i++) {
            if (s[i] == ')' && pos.empty() ) {
                s[i] = '0';
                continue;
            }
            if (s[i] == '(') {
                pos.push(i);
            } else if (s[i] == ')') {
                pos.pop();
            }
        }
        while (!pos.empty()) {
            s[pos.top()] = '0';
            pos.pop();
        }
    }
    string result;
    for (char c : s) {
        if (c != '0') {
            result += c;
        }
    }
    return result;
}
int main() {
    cout << minRemoveToMakeValid("lee(t(c)o)de)") << endl;
    cout << minRemoveToMakeValid("a)b(c)d") << endl;
    cout << minRemoveToMakeValid("))((") << endl;
}
Output:
lee(t(c)o)de
ab(c)d

Complexity

  • Runtime: O(N), where N = s.length.
  • Extra space: O(N).

Implementation notes

  • Since s contains only characters '(', ')' or lowercase English letters, you can use any other one to mark the skipped characters.
  • You can scope a piece of code between the pair { ... } to release some local memory (e.g. the stack pos) because the variable is destroyed at the end of the scope.