LeetCode 1910 Remove All Occurrences of a Substring - Optimal Solution using stack (Med, Java, O(n))
The algorithm uses a stack simulation with a character array to efficiently remove all occurrences of a substring. It processes the string character

I’m Anni Huang, an AI researcher-in-training currently at ByteDance, specializing in LLM training operations with a coding focus. I bridge the gap between engineering execution and model performance, ensuring the quality, reliability, and timely delivery of large-scale training projects.
Overall Strategy
The algorithm uses a stack simulation with a character array to efficiently remove all occurrences of a substring. It processes the string character by character and removes matching substrings as soon as they're detected.
Code Breakdown
Static Block (Warmup)
static {
for (int i = 0; i < 100; i++) {
removeOccurrences("daabcbaabcbc", "abc");
}
}
This runs the method 100 times during class loading to "warm up" the JVM - optimizing performance for online judges that run multiple test cases.
Main Algorithm
1. Setup:
char[] pt = part.toCharArray(); // Convert substring to char array
char[] stack = new char[s.length()]; // Stack simulation (max size = s.length)
int m = pt.length, size = 0; // m = substring length, size = stack size
char end = pt[m - 1]; // Last character of substring
2. Process each character:
for (char cur : s.toCharArray()) {
stack[size++] = cur; // Always add current character to stack
// Check if we might have a complete match
if (cur == end && size >= m) {
// Potential match found - verify backwards
}
}
3. Match verification (the tricky part):
if (cur == end && size >= m) {
int j = m - 1, k = size - j - 1; // j = pattern index, k = stack start position
while (j >= 0 && stack[k + j] == pt[j])
j--;
if (j < 0) size = j + k + 1; // Match found - "pop" the substring
}
How the Match Verification Works
Let's trace through an example: s = "daabcbaabcbc", part = "abc"
When we encounter 'c' (potential end of "abc"):
j = 2(last index of "abc")k = size - j - 1 = size - 3(start position to check in stack)- Compare backwards:
stack[k+2] == 'c',stack[k+1] == 'b',stack[k] == 'a' - If all match,
jbecomes-1, so we "pop" by settingsize = j + k + 1 = k
Visual Example
s = "daabcbaabcbc", part = "abc"
Step by step:
stack: [d] -> [d,a] -> [d,a,a] -> [d,a,a,b] -> [d,a,a,b,c]
↑
'c' matches end of "abc"
Check backwards: a,b,c ✓
Remove 3 chars: stack = [d,a]
Continue: [d,a,b] -> [d,a,b,a] -> [d,a,b,a,a] -> [d,a,b,a,a,b] -> [d,a,b,a,a,b,c]
↑
Check: a,b,c ✓
Remove: [d,a,b,a]
Continue: [d,a,b,a,b] -> [d,a,b,a,b,c]
↑
Check: a,b,c ✓
Remove: [d,a,b]
Final result: "dab"
Why It's Efficient
- Single Pass: O(n) time complexity - each character is processed once
- Space Efficient: Uses a character array instead of StringBuilder
- Early Detection: Only checks for matches when the last character matches
- No String Operations: Avoids expensive string creation during processing
This is much faster than the naive approach that repeatedly searches and replaces, especially for cases with many overlapping occurrences.




