Remove K-Balanced Substrings solution and explanation
7 more videos on this problem
Remove K-Balanced Substrings | LeetCode 3703 | Most Optimal SolutionSanyam IIT Guwahati · 16:01 · 2K views
3703. Remove K-Balanced Substrings (Leetcode Medium)Programming Live with Larry · 14:35 · 442 views
Remove K-Balanced Substrings | LeetCode 3703 | weekly contest 470 | Java Code | Developer CoderDeveloper Coder · 31:52 · 378 views
Leetcode 3703 | Remove K-Balanced SubstringsCodeWithMeGuys · 11:26 · 370 views
Leetcode | 3703 Remove K Balanced SubStrings | Java | Deque | Weekly Contest - 470Cakot Coding · 22:12 · 315 views
LeetCode Weekly Contest 470 | Problem 3703: Remove K-Balanced Substrings ExplainedDesiCoding · 16:05 · 177 views
Leetcode Weekly 470 | Remove K Balanced Sub-Strings | StackAlgonaline · 25:53 · 163 views
Remove K-Balanced Substrings | Leetcode 3703 | Weekly Contest 470ExpertFunda · 22:13 · 65 views
The problem
You are given a string s consisting of '(' and ')', and an integer k.
A string is k-balanced if it is exactly k consecutive '(' followed by k consecutive ')', i.e., '(' * k + ')' * k.
For example, if k = 3, k-balanced is "((()))".
You must repeatedly remove all non-overlapping k-balanced substrings from s, and then join the remaining parts. Continue this process until no k-balanced substring exists.
Return the final string after all possible removals.
Example 1:
Input: s = "(())", k = 1
Output: ""
Explanation:
k-balanced substring is "()"
| Step | Current s |
k-balanced |
Result s |
|---|---|---|---|
| 1 | (()) |
( |
() |
| 2 | () |
() |
Empty |
Thus, the final string is "".
Example 2:
Input: s = "(()(", k = 1
Output: "(("
Explanation:
k-balanced substring is "()"
| Step | Current s |
k-balanced |
Result s |
|---|---|---|---|
| 1 | (()( |
( |
(( |
| 2 | (( |
- | (( |
Thus, the final string is "((".
Example 3:
Input: s = "((()))()()()", k = 3
Output: "()()()"
Explanation:
k-balanced substring is "((()))"
| Step | Current s |
k-balanced |
Result s |
|---|---|---|---|
| 1 | ((()))()()() |
|
()()() |
| 2 | ()()() |
- | ()()() |
Thus, the final string is "()()()".
Constraints:
2 <= s.length <= 105sconsists only of'('and')'.1 <= k <= s.length / 2
Approach overview
Problem Overview: You are given a string and an integer k. The task is to repeatedly remove substrings that are k-balanced until no more such substrings exist. After each removal, the remaining parts of the string join together, which may create new k-balanced segments. The goal is to simulate this process efficiently and return the final string.
Approach 1: Brute Force Simulation (O(n^2) time, O(n) space)
The most direct strategy repeatedly scans the string to detect substrings that satisfy the k-balanced condition. Once a valid segment is found, remove it and restart scanning because the merge may create new removable substrings. Implementation usually builds new strings or uses substring operations. The major cost comes from repeated rescans and string reconstruction, leading to O(n^2) time in the worst case and O(n) auxiliary space. This approach works for small inputs but quickly becomes too slow for interview constraints.
Approach 2: Stack-Based Simulation (O(n) time, O(n) space)
A more efficient strategy uses a stack to simulate the removals while scanning the string once. Push characters onto the stack and track the frequency balance of the current segment. Whenever the top portion of the stack forms a k-balanced substring, pop those characters immediately. This mirrors the effect of removing the substring and letting the remaining characters connect. Because each character is pushed and popped at most once, the total work is linear. The stack naturally models the dynamic merging behavior without repeatedly rebuilding strings.
The key insight is that substring removal problems often behave like bracket cancellation. By storing partial results in a stack, you only examine newly formed boundaries when characters are added or removed. This makes the algorithm a classic simulation problem combined with stack operations on a string.
Recommended for interviews: The stack-based solution is the expected approach. Interviewers want to see that you recognize the repeated-removal pattern and avoid rebuilding the string after every deletion. Explaining the brute force approach first shows understanding of the process, while transitioning to a stack demonstrates the optimization that reduces the complexity to linear time.
Solution
We use a stack to maintain the current state of the string. Each element in the stack is a pair representing a character and its consecutive count.
Traverse each character in the string:
- If the stack is not empty and the character of the top element matches the current character, increment the count of the top element.
- Otherwise, push the current character with count 1 as a new element onto the stack.
- If the current character is
')', and there are at least two elements in the stack, and the count of the top element equalsk, and the count of the previous element is greater than or equal tok, then pop the top element and subtractkfrom the count of the previous element. If the count of the previous element becomes 0, pop it as well.
After traversal, the remaining elements in the stack represent the final state of the string. We concatenate these elements in order to get the result string.
The time complexity is O(n) and the space complexity is O(n), where n is the length of string s.
class Solution:
def removeSubstring(self, s: str, k: int) -> str:
stk = []
for c in s:
if stk and stk[-1][0] == c:
stk[-1][1] += 1
else:
stk.append([c, 1])
if c == ")" and len(stk) > 1 and stk[-1][1] == k and stk[-2][1] >= k:
stk.pop()
stk[-1][1] -= k
if stk[-1][1] == 0:
stk.pop()
return "".join(c * v for c, v in stk)class Solution {
public String removeSubstring(String s, int k) {
List<int[]> stk = new ArrayList<>();
for (char c : s.toCharArray()) {
if (!stk.isEmpty() && stk.get(stk.size() - 1)[0] == c) {
stk.get(stk.size() - 1)[1] += 1;
} else {
stk.add(new int[] {c, 1});
}
if (c == ')' && stk.size() > 1) {
int[] top = stk.get(stk.size() - 1);
int[] prev = stk.get(stk.size() - 2);
if (top[1] == k && prev[1] >= k) {
stk.remove(stk.size() - 1);
prev[1] -= k;
if (prev[1] == 0) {
stk.remove(stk.size() - 1);
}
}
}
}
StringBuilder sb = new StringBuilder();
for (int[] pair : stk) {
for (int i = 0; i < pair[1]; i++) {
sb.append((char) pair[0]);
}
}
return sb.toString();
}
}class Solution {
public:
string removeSubstring(string s, int k) {
vector<pair<char, int>> stk;
for (char c : s) {
if (!stk.empty() && stk.back().first == c) {
stk.back().second += 1;
} else {
stk.emplace_back(c, 1);
}
if (c == ')' && stk.size() > 1) {
auto& top = stk.back();
auto& prev = stk[stk.size() - 2];
if (top.second == k && prev.second >= k) {
stk.pop_back();
prev.second -= k;
if (prev.second == 0) {
stk.pop_back();
}
}
}
}
string res;
for (auto& p : stk) {
res.append(p.second, p.first);
}
return res;
}
};func removeSubstring(s string, k int) string {
type pair struct {
ch byte
count int
}
stk := make([]pair, 0)
for i := 0; i < len(s); i++ {
c := s[i]
if len(stk) > 0 && stk[len(stk)-1].ch == c {
stk[len(stk)-1].count++
} else {
stk = append(stk, pair{c, 1})
}
if c == ')' && len(stk) > 1 {
top := &stk[len(stk)-1]
prev := &stk[len(stk)-2]
if top.count == k && prev.count >= k {
stk = stk[:len(stk)-1]
prev.count -= k
if prev.count == 0 {
stk = stk[:len(stk)-1]
}
}
}
}
res := make([]byte, 0)
for _, p := range stk {
for i := 0; i < p.count; i++ {
res = append(res, p.ch)
}
}
return string(res)
}Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Brute Force Simulation | O(n^2) | O(n) | Useful for understanding the removal process or when input size is small |
| Stack-Based Simulation | O(n) | O(n) | Optimal approach for interviews and large inputs |
Frequently asked questions
Is Remove K-Balanced Substrings easy or hard?
Remove K-Balanced Substrings is generally considered a medium-level problem. The challenge comes from recognizing that repeated substring removals should be simulated with a stack instead of repeatedly modifying the string. Once that insight is clear, the implementation becomes straightforward.
Remove K-Balanced Substrings Python/Java solution
The typical implementation uses a stack structure such as a Python list or Java Deque. Iterate through the string, push characters, maintain balance information, and pop characters when a k-balanced substring appears. The same algorithm translates directly to C++, Go, Python, or Java.
How to solve Remove K-Balanced Substrings in O(n)?
Scan the string from left to right while maintaining a stack of characters. Track the balance condition required for a substring to be k-balanced. Whenever the top portion of the stack forms such a substring, remove it immediately. This avoids repeated rescanning and keeps the overall runtime linear.
What is the best approach for Remove K-Balanced Substrings?
The most efficient approach uses a stack to simulate substring removals while scanning the string once. Characters are pushed onto the stack and removed when they form a k-balanced segment. Each character is processed at most twice (push and pop), resulting in O(n) time complexity and O(n) space.
Is Remove K-Balanced Substrings asked at Google/Amazon/Meta?
Problems involving repeated substring removal, stack simulation, and string balancing appear frequently in interviews at companies like Google, Amazon, and Meta. Variants such as removing adjacent duplicates or simplifying strings using stacks follow the same core pattern.
What data structure is used in Remove K-Balanced Substrings?
A stack is the primary data structure used to solve this problem efficiently. The stack stores characters of the partially processed string and allows quick removal when a k-balanced substring forms. Additional counters or frequency tracking help detect when the condition is satisfied.
What is the time complexity of Remove K-Balanced Substrings?
The optimal stack-based solution runs in O(n) time where n is the length of the string. Each character is pushed to the stack once and popped at most once. The space complexity is O(n) because the stack may store the remaining characters.
