Back to blog
Jun 27, 2025
5 min read

Remove Outermost Parentheses

Remove the outermost parentheses from every primitive string in the decomposition of a valid parentheses string.

Difficulty: Easy | Acceptance: 87.00% | Paid: No Topics: String, Stack

A valid parentheses string is either empty "", ”(” + A + ”)”, or A + B, where A and B are valid parentheses strings, and A and B have no common leading parentheses. The primitive decomposition of a valid parentheses string S is a decomposition: S = P_1 + P_2 + … + P_k, where P_i are primitive valid parentheses strings.

Return S after removing the outermost parentheses of every primitive string in the primitive decomposition of S.

Examples

Input: s = "(()())(())"
Output: "()()()"
Explanation: 
The input string is "(()())(())", with primitive decomposition "(()())" + "(())".
After removing outer parentheses of each part, this is "()()" + "()" = "()()()".
Input: s = "(()())(())(()(()))"
Output: "()()()()(())"
Explanation: 
The input string is "(()())(())(()(()))", with primitive decomposition "(()())" + "(())" + "(()(()))".
After removing outer parentheses of each part, this is "()()" + "()" + "()(())" = "()()()()(())".
Input: s = "()()"
Output: ""
Explanation: 
The input string is "()()", with primitive decomposition "()" + "()".
After removing outer parentheses of each part, this is "" + "" = "".

Constraints

1 <= s.length <= 10⁵
s[i] is either '(' or ')'.
s is a valid parentheses string.

Approach 1: Counting (Depth Tracking)

Intuition We can iterate through the string and maintain a counter representing the current depth of nesting. The outermost parentheses correspond to a depth of 1. We only append characters to the result when the depth is greater than 1.

Steps

  • Initialize a counter opened to 0 and an empty list/string builder for the result.
  • Iterate through each character in the string.
  • If the character is ’(’, increment opened. If opened is greater than 1 after incrementing, append the character to the result.
  • If the character is ’)’, decrement opened. If opened is greater than 0 after decrementing, append the character to the result.
  • Return the constructed result string.
python
class Solution:
    def removeOuterParentheses(self, s: str) -&gt; str:
        res = []
        opened = 0
        for c in s:
            if c == '(':
                if opened &gt; 0:
                    res.append(c)
                opened += 1
            else:
                opened -= 1
                if opened &gt; 0:
                    res.append(c)
        return ''.join(res)

Complexity

  • Time: O(n)
  • Space: O(1) auxiliary space (excluding the space required for the output string).
  • Notes: This is the most space-efficient approach as it only requires a single integer variable for tracking.

Approach 2: Stack

Intuition We can use a stack to simulate the nesting of parentheses. We push opening brackets onto the stack. When we encounter a closing bracket, we pop from the stack. If the stack is not empty, it means we are inside a primitive string, and we should add the character to our result.

Steps

  • Initialize an empty stack and an empty list/string builder for the result.
  • Iterate through each character in the string.
  • If the character is ’(’, check if the stack is not empty. If so, append the character to the result. Then push the character onto the stack.
  • If the character is ’)’, pop from the stack. If the stack is not empty, append the character to the result.
  • Return the constructed result string.
python
class Solution:
    def removeOuterParentheses(self, s: str) -&gt; str:
        res = []
        stack = []
        for c in s:
            if c == '(':
                if stack:
                    res.append(c)
                stack.append(c)
            else:
                stack.pop()
                if stack:
                    res.append(c)
        return ''.join(res)

Complexity

  • Time: O(n)
  • Space: O(n) in the worst case for the stack.
  • Notes: While intuitive, the stack approach uses more memory than the counting approach, though both have linear time complexity.

Approach 3: Primitive Decomposition

Intuition We can identify the boundaries of each primitive string by tracking the balance of parentheses. When the balance returns to zero, we have found the end of a primitive. We then extract the substring excluding the first and last characters.

Steps

  • Initialize a result list/string builder, a start index set to 0, and a balance counter set to 0.
  • Iterate through the string with an index.
  • Increment balance for ’(’ and decrement for ’)‘.
  • If balance becomes 0, we have reached the end of a primitive. Append the substring from start + 1 to the current index (exclusive) to the result. Update start to the next index.
  • Return the joined result string.
python
class Solution:
    def removeOuterParentheses(self, s: str) -&gt; str:
        res = []
        start = 0
        bal = 0
        for i, c in enumerate(s):
            if c == '(':
                bal += 1
            else:
                bal -= 1
            if bal == 0:
                res.append(s[start+1:i])
                start = i + 1
        return ''.join(res)

Complexity

  • Time: O(n)
  • Space: O(n) for the result string.
  • Notes: This approach directly follows the problem definition of primitive decomposition but involves string slicing which can be slightly less efficient than character-by-character building in some languages.