Remove Outermost Parentheses solution and explanation
9 more videos on this problem
1021. Remove Outermost Parentheses | Leetcode | Easy Problem | Strings | Stacks | Counter ApproachFastForward Coders - by Gurmeet · 19:51 · 63K views
Leetcode 1021 - | Stack | Remove Outer ParanthesesNideesh Terapalli · 11:45 · 17K views
1021. Remove Outermost Parentheses (Leetcode Easy)Programming Live with Larry · 9:17 · 3K views
1021. Remove Outermost Parentheses | Leetcode | Easy Problem | JavaScript SolutionWeb Coder Abhishek · 2:08 · 3K views
[Easy] Remove Outermost Parentheses (leetcode #1021, javascript, strings, arrays)Build Things That Work · 23:52 · 1K views
1021. Remove Outermost Parentheses || LEETCODE SOLUTION || IN C++ || DATA STRUCTURE AND ALGORITHMAbhishweta Gupta · 2:45 · 1K views
Leetcode 1021. Remove Outermost Parentheses (Solution + Explanation)ultcode · 4:05 · 619 views
LeetCode 1021 - Remove Outermost Parentheses: A Microsoft JourneyThe Lofi Dev · 5:18 · 493 views
LeetCode 1021. Remove Outermost Parentheses JavaScript SolutionLearn Code · 1:32 · 473 views
Remove Outermost Parentheses (LeetCode 1021)BitsToBytes · 10:45 · 449 views
The problem
A valid parentheses string is either empty "", "(" + A + ")", or A + B, where A and B are valid parentheses strings, and + represents string concatenation.
- For example,
"","()","(())()", and"(()(()))"are all valid parentheses strings.
A valid parentheses string s is primitive if it is nonempty, and there does not exist a way to split it into s = A + B, with A and B nonempty valid parentheses strings.
Given a valid parentheses string s, consider its primitive decomposition: s = P1 + P2 + ... + Pk, where Pi are primitive valid parentheses strings.
Return s after removing the outermost parentheses of every primitive string in the primitive decomposition of s.
Example 1:
Input: s = "(()())(())" Output: "()()()" Explanation: The input string is "(()())(())", with primitive decomposition "(()())" + "(())". After removing outer parentheses of each part, this is "()()" + "()" = "()()()".
Example 2:
Input: s = "(()())(())(()(()))" Output: "()()()()(())" Explanation: The input string is "(()())(())(()(()))", with primitive decomposition "(()())" + "(())" + "(()(()))". After removing outer parentheses of each part, this is "()()" + "()" + "()(())" = "()()()()(())".
Example 3:
Input: s = "()()" Output: "" Explanation: The input string is "()()", with primitive decomposition "()" + "()". After removing outer parentheses of each part, this is "" + "" = "".
Constraints:
1 <= s.length <= 105s[i]is either'('or')'.sis a valid parentheses string.
Approach overview
Problem Overview: You are given a valid parentheses string s. The string can be split into multiple primitive valid parentheses substrings. The task is to remove the outermost pair of parentheses from every primitive part and return the final combined string.
Approach 1: Using a Counter to Track Depth (O(n) time, O(1) space)
This approach tracks the current nesting depth while iterating through the string once. Increment the depth when encountering '(' and decrement it when encountering ')'. The key insight: characters should only be added to the result when they are not the outermost parentheses. For an opening bracket, append it only if the current depth is already greater than zero before incrementing. For a closing bracket, decrement first and append only if the depth remains greater than zero. This effectively strips the first and last parentheses of each primitive group without explicitly splitting the string. Since the algorithm performs a single pass and only maintains an integer counter, it runs in O(n) time with O(1) extra space. This method is the most common solution when working with string traversal problems involving balanced parentheses.
Approach 2: Stack-Based Iteration (O(n) time, O(n) space)
A more explicit method uses a stack to simulate the structure of nested parentheses. Push an index or character when encountering '(' and pop when encountering ')'. The stack size represents the current nesting level. When processing characters, skip the parentheses that correspond to the outermost layer of each primitive substring. In practice, append characters only when the stack size after the operation indicates you are inside a nested level. While this approach mirrors how many stack-based parsing algorithms work, it uses additional memory proportional to the nesting depth. The runtime remains O(n) because each character is pushed and popped at most once.
Recommended for interviews: The depth counter solution is the expected answer. It shows you recognize that the stack structure is unnecessary because the nesting level alone determines whether a parenthesis is outermost. The stack approach demonstrates understanding of stack-driven parsing, but the counter approach is cleaner, uses constant space, and signals strong problem-solving instincts.
Approach 1: Approach 1: Using a Counter to Track Depth
This approach uses a counter to track the depth of the current parentheses level. As you iterate through the string, increment the count whenever you see '(', and decrement it on ')'. The outermost parentheses of a primitive section occur whenever the depth transitions from 0 to 1 (for '(') or from 1 to 0 (for ')'). These should be skipped in your final result.
def removeOuterParentheses(s: str) -> str:
level = 0
result = []
for c in s:
if c == '(':
if level > 0:
result.append(c)
level += 1
else: # c == ')'
level -= 1
if level > 0:
result.append(c)
return ''.join(result)
print('Output:', removeOuterParentheses("(()())(())"))public class Solution {
public String removeOuterParentheses(String s) {
int level = 0;
StringBuilder result = new StringBuilder();
for (char c : s.toCharArray()) {
if (c == '(') {
if (level > 0) result.append(c);
level++;
} else {
level--;
if (level > 0) result.append(c);
}
}
return result.toString();
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println("Output: " + sol.removeOuterParentheses("(()())(())"));
}
}#include <string>
std::string removeOuterParentheses(std::string s) {
int level = 0;
std::string result;
for (char c : s) {
if (c == '(') {
if (level++ > 0) result += c;
} else {
if (--level > 0) result += c;
}
}
return result;
}
#include <iostream>
int main() {
std::string s = "(()())(())";
std::cout << "Output: " << removeOuterParentheses(s) << std::endl;
return 0;
}function removeOuterParentheses(s) {
let level = 0;
let result = '';
for (let char of s) {
if (char === '(') {
if (level > 0) result += char;
level++;
} else {
level--;
if (level > 0) result += char;
}
}
return result;
}
console.log('Output:', removeOuterParentheses('(()())(())'));#include <stdio.h>
#include <string.h>
char* removeOuterParentheses(char* s) {
int level = 0;
int index = 0;
char *result = (char *)malloc(strlen(s) * sizeof(char));
for (int i = 0; s[i] != '\0'; ++i) {
if (s[i] == '(') {
if (level > 0) {
result[index++] = s[i];
}
level++;
} else { // s[i] == ')'
level--;
if (level > 0) {
result[index++] = s[i];
}
}
}
result[index] = '\0';
return result;
}
int main() {
char s[] = "(()())(())";
printf("Output: %s\n", removeOuterParentheses(s));
return 0;
}using System;
using System.Text;
public class Solution {
public string RemoveOuterParentheses(string s) {
int level = 0;
StringBuilder result = new StringBuilder();
foreach (char c in s) {
if (c == '(') {
if (level > 0) result.Append(c);
level++;
} else {
level--;
if (level > 0) result.Append(c);
}
}
return result.ToString();
}
public static void Main(string[] args) {
Solution sol = new Solution();
Console.WriteLine("Output: " + sol.RemoveOuterParentheses("(()())(())"));
}
}In the Python solution, a list is used to accumulate the resultant characters for efficiency of append operations. Post iteration, the list is converted to a string using ''.join(result) to produce the final output string.
Time Complexity: O(n), where n is the length of the input string.
Space Complexity: O(n) for collecting the result.
Approach 2: Approach 2: Stack-Based Iteration
This approach leverages a stack-like mechanism to mimic the actual stack process of valid parentheses. Instead of using a stack data structure, it simply uses a counter to determine valid scope levels, mimicking pushing and popping operations.
def removeOuterParentheses(s: str) -> str:
openCount = 0
result = []
for c in s:
if c == '(':
if openCount > 0:
result.append(c)
openCount += 1
else:
openCount -= 1
if openCount > 0:
result.append(c)
return ''.join(result)
print(removeOuterParentheses('(()())(())(()(()))'))public class Solution {
public String removeOuterParentheses(String s) {
StringBuilder result = new StringBuilder();
int openCount = 0;
for (char c : s.toCharArray()) {
if (c == '(') {
if (openCount > 0) {
result.append(c);
}
openCount++;
} else {
openCount--;
if (openCount > 0) {
result.append(c);
}
}
}
return result.toString();
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.removeOuterParentheses("(()())(())(()(()))"));
}
}#include <iostream>
#include <string>
std::string removeOuterParentheses(std::string s) {
std::string result;
int openCount = 0;
for (char c : s) {
if (c == '(') {
if (openCount > 0) result += c;
openCount++;
} else {
openCount--;
if (openCount > 0) result += c;
}
}
return result;
}
int main() {
std::string s = "(()())(())(()(()))";
std::cout << removeOuterParentheses(s) << std::endl;
return 0;
}function removeOuterParentheses(s) {
let openCount = 0;
let result = '';
for (let char of s) {
if (char === '(') {
if (openCount > 0) {
result += char;
}
openCount++;
} else {
openCount--;
if (openCount > 0) {
result += char;
}
}
}
return result;
}
console.log(removeOuterParentheses('(()())(())(()(()))'));#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char* removeOuterParentheses(char* s) {
char* result = (char*)malloc(strlen(s) * sizeof(char));
int index = 0;
int openCount = 0;
for (int i = 0; s[i] != '\0'; i++) {
if (s[i] == '(') {
if (openCount > 0) {
result[index++] = s[i];
}
openCount++;
} else {
openCount--;
if (openCount > 0) {
result[index++] = s[i];
}
}
}
result[index] = '\0';
return result;
}
int main() {
char s[] = "(()())(())";
char *result = removeOuterParentheses(s);
printf("%s\n", result);
free(result);
return 0;
}using System;
using System.Text;
public class Solution {
public string RemoveOuterParentheses(string s) {
int openCount = 0;
StringBuilder result = new StringBuilder();
foreach (char c in s) {
if (c == '(') {
if (openCount > 0) {
result.Append(c);
}
openCount++;
} else {
openCount--;
if (openCount > 0) {
result.Append(c);
}
}
}
return result.ToString();
}
public static void Main(string[] args) {
Solution solution = new Solution();
Console.WriteLine(solution.RemoveOuterParentheses("(()())(())(()(()))"));
}
}This Python version uses a list to ease appending operations and an openCount to track nested validity. When a character transitions beyond the count's outer or inner limit, it is excluded, dropping the outer pairs.
Time Complexity: O(n), requiring just a singular pass across the string.
Space Complexity: O(n) to capture the resultant string.
Approach 3: Default Approach
function removeOuterParentheses(s: string): string {
let res = '';
let depth = 0;
for (const c of s) {
if (c === '(') {
depth++;
}
if (depth !== 1) {
res += c;
}
if (c === ')') {
depth--;
}
}
return res;
}func removeOuterParentheses(s string) string {
ans := []rune{}
cnt := 0
for _, c := range s {
if c == '(' {
cnt++
if cnt > 1 {
ans = append(ans, c)
}
} else {
cnt--
if cnt > 0 {
ans = append(ans, c)
}
}
}
return string(ans)
}impl Solution {
pub fn remove_outer_parentheses(s: String) -> String {
let mut res = String::new();
let mut depth = 0;
for c in s.chars() {
if c == '(' {
depth += 1;
}
if depth != 1 {
res.push(c);
}
if c == ')' {
depth -= 1;
}
}
res
}
}Complexity comparison
| Approach | Complexity |
|---|---|
| Approach 1: Using a Counter to Track Depth | Time Complexity: O(n), where n is the length of the input string. |
| Approach 2: Stack-Based Iteration | Time Complexity: O(n), requiring just a singular pass across the string. |
| Default Approach | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Counter to Track Depth | O(n) | O(1) | Best choice for interviews and production code when only nesting depth matters |
| Stack-Based Iteration | O(n) | O(n) | Useful when explicitly modeling nested structure or extending to complex parsing problems |
Frequently asked questions
Is Remove Outermost Parentheses easy or hard?
Remove Outermost Parentheses is classified as an Easy problem on LeetCode with a high acceptance rate. The main challenge is recognizing that tracking nesting depth is enough to identify and remove the outermost parentheses from each primitive substring.
Remove Outermost Parentheses Python/Java solution
In Python or Java, iterate through the string while tracking depth. Append characters only when the current nesting level is not the outermost. The implementation typically uses a string builder or list for efficient concatenation and completes in O(n) time.
How to solve Remove Outermost Parentheses in O(n)?
Iterate through the string while maintaining a nesting depth counter. When encountering '(', append it only if the current depth is already greater than zero, then increment the depth. When encountering ')', decrement the depth first and append it only if the new depth remains greater than zero. This removes the outermost layer from each primitive substring.
What is the best approach for Remove Outermost Parentheses?
The best approach uses a depth counter while scanning the string once. Increase the depth for '(' and decrease it for ')', but only append characters when the depth indicates they are not part of the outermost layer. This solution runs in O(n) time and O(1) extra space.
Is Remove Outermost Parentheses asked at Google/Amazon/Meta?
This problem represents a common pattern used in technical interviews involving parentheses parsing and stack simulation. Variations of balanced parentheses processing frequently appear in interviews at companies like Amazon, Google, and Meta.
What data structure is used in Remove Outermost Parentheses?
The problem can be solved with either a simple integer counter or a stack. The counter tracks the current nesting depth of parentheses, while the stack explicitly stores opening brackets during traversal. Most optimized solutions prefer the counter because it reduces space complexity.
What is the time complexity of Remove Outermost Parentheses?
The optimal solution runs in O(n) time where n is the length of the string. Each character is processed exactly once while maintaining a simple depth counter or stack. Space complexity is O(1) for the counter approach and O(n) for the stack approach.
