1910. Remove All Occurrences of a Substring - Detailed Explanation

Free Coding Questions Catalog
Boost your coding skills with our essential coding questions catalog. Take a step towards a better tech career now!

Problem Statement

Description:
You are given two strings, s and part. The task is to remove every occurrence of the substring part from s. The removal should be done repeatedly; that is, after you remove an occurrence, the remaining string might form a new occurrence of part that must also be removed. Continue this process until s no longer contains part as a substring, and return the final string.

Example 1:

  • Input:
    s = "daabcbaabcbc", part = "abc"
  • Output:
    "dab"
  • Explanation:
    1. The first occurrence of "abc" appears at index 2 ("daabcbaabcbc"). Remove it, and the string becomes "dabaabcbc".
    2. Now, the next occurrence of "abc" appears at index 4 ("dabaabcbc"). Remove it to obtain "dababc".
    3. Finally, an occurrence appears at index 3 ("dababc"). Remove it to get "dab".
    4. No more occurrences of "abc" remain.

Example 2:

  • Input:
    s = "axxxxyyyyb", part = "xy"
  • Output:
    "axxxxyyyyb"
  • Explanation:
    In this example, if removing "xy" does not lead to a new occurrence or if the given removal rules do not trigger further changes, then the final string is returned as is. (This example depends on the exact removal order; typically, you’d remove occurrences until none remain.)

Constraints:

  • The lengths of s and part can be up to moderate sizes (commonly within a few thousand characters), so an efficient solution is desired.
  • s and part consist of lowercase English letters (typically).

Hints

  • Repetitive Removal:
    Notice that after you remove an occurrence of part, the remaining parts of s might come together to form a new occurrence. You must check repeatedly until no occurrence remains.

  • Using a Stack:
    A common efficient technique is to simulate the removals by using a stack (or building a result string). As you iterate over s, append characters to a result (or stack). Whenever the end of the result matches part, remove that portion. This way, you "cancel out" occurrences on the fly.

  • String Methods:
    Although you can use built-in string methods (like find and replace in a loop), they might be less efficient if s is very long. The stack approach ensures you process each character only once.

Approaches

Approach 1: Iterative Removal Using Built-in Functions

  1. While Loop with find:
    Use a loop that continues while s contains part. Inside the loop, use s.find(part) to locate the first occurrence, remove it by slicing, and update s.
  2. Drawback:
    Each removal may scan the string, resulting in potentially (O(n^2)) performance in the worst-case.

Approach 2: Stack/Result-Building Simulation (Preferred)

  1. Iterate Over Characters:
    Use a stack (or a dynamic string builder) to accumulate characters from s.
  2. Check Suffix:
    After adding each character, check if the current ending of the built string equals part.
  3. Remove on Match:
    If it does, remove the last (\text{len(part)}) characters from the result.
  4. Return Final String:
    Once all characters are processed, the result is the final string.

This approach runs in (O(n \times m)) in the worst-case, where (m) is the length of part, but in practice it is very efficient.

Code Implementations

Python Implementation

Python3
Python3

. . . .

Java Implementation

Java
Java

. . . .

Step-by-Step Walkthrough (Stack Approach)

Consider s = "daabcbaabcbc" and part = "abc".

  1. Initialize:
    Create an empty string (or list as stack) called result.

  2. Process Each Character:

    • Index 0:

      • Character: 'd'
      • result becomes: "d"
      • Check: Does "d" end with "abc"? No.
    • Index 1:

      • Character: 'a'
      • result becomes: "da"
      • Check: "da" does not end with "abc".
    • Index 2:

      • Character: 'a'
      • result becomes: "daa"
      • Check: "daa""abc".
    • Index 3:

      • Character: 'b'
      • result becomes: "daab"
      • Check: Last 3 characters "aab""abc".
    • Index 4:

      • Character: 'c'
      • result becomes: "daabc"
      • Check: Last 3 characters: "abc" (matches part).
      • Action: Remove the last 3 characters.
        New result becomes: "da".
    • Index 5:

      • Character: 'b'
      • result becomes: "dab"
      • Check: "dab""abc".
    • Index 6:

      • Character: 'a'
      • result becomes: "daba"
      • Check: Last 3 characters "aba""abc".
    • Index 7:

      • Character: 'a'
      • result becomes: "dabaa"
      • Check: "baa""abc".
    • Index 8:

      • Character: 'b'
      • result becomes: "dabaab"
      • Check: Last 3 characters "aab""abc".
    • Index 9:

      • Character: 'c'
      • result becomes: "dabaabc"
      • Check: Last 3 characters "abc" (matches part).
      • Action: Remove them.
        New result becomes: "daba".
    • Index 10:

      • Character: 'b'
      • result becomes: "dabab"
      • Check: Last 3 characters "bab""abc".
    • Index 11:

      • Character: 'c'
      • result becomes: "dababc"
      • Check: Last 3 characters "abc" (matches part).
      • Action: Remove them.
        New result becomes: "dab".
  3. Final Result:
    After processing all characters, the final string is "dab".

Complexity Analysis

  • Time Complexity:
    In the worst-case, each character is added and may trigger a check that takes (O(m)) time (where (m) is the length of part). Overall, worst-case time is (O(n \times m)). However, many practical inputs perform better.

  • Space Complexity:
    (O(n)) for storing the result (or using a stack).

Common Mistakes

  • Not Checking After Each Addition:
    Failing to check whether the current result ends with part immediately after appending a character.

  • Incorrect Substring Removal:
    Removing the wrong number of characters or using an off-by-one index error when checking the suffix.

  • Inefficient Repeated Searches:
    Using an approach that repeatedly calls a global search (like s.find(part)) without taking advantage of the incremental building of the result.

Edge Cases

  • No Occurrence of Part:
    If part is not in s, the final string is s itself.

  • Entire String is Part:
    If s equals part (or is made up of repeated part), the final result should be an empty string.

  • Overlapping Occurrences:
    The stack approach naturally handles overlapping cases by removing immediately when a complete occurrence is found.

Relevant Problems

If you enjoyed this problem, you might also like:

  • Remove All Adjacent Duplicates in String:
    Similar idea of using a stack to remove duplicates.

  • String Compression:
    Involves processing strings and reducing them based on repetition.

  • Decode String:
    Uses a stack to process nested encoded strings.

  • Longest Substring Without Repeating Characters:
    Focuses on processing substrings with a sliding window/stack approach.

TAGS
leetcode
CONTRIBUTOR
Design Gurus Team
-

GET YOUR FREE

Coding Questions Catalog

Design Gurus Newsletter - Latest from our Blog
Boost your coding skills with our essential coding questions catalog.
Take a step towards a better tech career now!
Explore Answers
Rehearsing mental execution of code for error detection
How valuable is coding bootcamp?
What to do in a coding interview if you don't know the answer?
Related Courses
Image
Grokking the Coding Interview: Patterns for Coding Questions
Grokking the Coding Interview Patterns in Java, Python, JS, C++, C#, and Go. The most comprehensive course with 476 Lessons.
Image
Grokking Modern AI Fundamentals
Master the fundamentals of AI today to lead the tech revolution of tomorrow.
Image
Grokking Data Structures & Algorithms for Coding Interviews
Unlock Coding Interview Success: Dive Deep into Data Structures and Algorithms.
Image
One-Stop Portal For Tech Interviews.
Copyright © 2025 Design Gurus, LLC. All rights reserved.
;