Remove a Character from a String: In-Place, Two-Pointer, and Stream Variants
Removing characters from a string is a foundational interview problem that tests in-place manipulation, two-pointer technique, and the realization that strings are immutable in many languages. The base problem — remove all occurrences of a target character — is simple enough to solve in 30 seconds. Interviewers use it as a warm-up, then push into harder variants: remove duplicates, remove characters matching a set, remove the kth occurrence, or stream-based filtering. This guide covers all the standard variants with working code.
Problem Statement
Given a string s and a character c, return a new string with all occurrences of c removed.
Examples:
s = "hello world",c = 'l'→"heo word"s = "abc",c = 'd'→"abc"s = "", any c →""
Approach 1: List Comprehension / String Builder
Idiomatic in Python: filter and join.
def remove_char(s: str, c: str) -> str:
"""O(n) time, O(n) space — idiomatic Python."""
return "".join(ch for ch in s if ch != c)
# Equivalent
def remove_char_alt(s: str, c: str) -> str:
return s.replace(c, "")
str.replace is the practical answer in Python. Complexity: O(n) time, O(n) space (new string).
For interview purposes, write the comprehension version to demonstrate you can solve it manually, then mention replace.
Approach 2: In-Place with Two Pointers (For Mutable Arrays)
If the string is given as a mutable character array (Java, C++, or a list-of-characters in Python), use two pointers to remove in place. Read pointer scans the array; write pointer fills positions to keep.
def remove_char_inplace(chars: list[str], c: str) -> int:
"""Modify chars in place; return new length.
O(n) time, O(1) extra space."""
write = 0
for read in range(len(chars)):
if chars[read] != c:
chars[write] = chars[read]
write += 1
return write
# Test
chars = list("hello world")
new_len = remove_char_inplace(chars, "l")
print("".join(chars[:new_len])) # "heo word"
Complexity: O(n) time, O(1) space. The standard answer when interviewers specify “in place” or “no extra string allocation.”
Approach 3: Filter Multiple Characters
Generalize: remove all characters matching a set. Useful follow-up.
def remove_chars(s: str, to_remove: set) -> str:
"""O(n) time, O(n) space."""
return "".join(ch for ch in s if ch not in to_remove)
# Test
print(remove_chars("hello world", {"l", "o"})) # "he wrd"
Use set for O(1) lookups. str.translate in Python is the optimized stdlib alternative:
def remove_chars_translate(s: str, to_remove: str) -> str:
"""O(n) time using str.translate."""
return s.translate(str.maketrans("", "", to_remove))
Approach 4: Remove Duplicates
(LeetCode #316, #1047 — Remove Adjacent Duplicates) Remove consecutive duplicate characters using a stack.
def remove_adjacent_duplicates(s: str) -> str:
"""O(n) time, O(n) space — stack-based."""
stack = []
for ch in s:
if stack and stack[-1] == ch:
stack.pop()
else:
stack.append(ch)
return "".join(stack)
# Tests
print(remove_adjacent_duplicates("abbaca")) # "ca"
print(remove_adjacent_duplicates("aaaa")) # ""
The stack pattern repeatedly cancels adjacent equal characters, naturally handling the “after removal, new adjacencies appear” case.
Approach 5: Stream Filtering
For very large strings or true streaming use cases, generators avoid loading the full string in memory.
def filter_stream(stream, c):
"""Generator that yields characters from stream, skipping c."""
for ch in stream:
if ch != c:
yield ch
# Usage with file streaming
# with open("large.txt") as f:
# filtered = "".join(filter_stream(f.read(), "l"))
The interview rarely demands streaming, but mentioning the generator approach signals memory awareness.
Common Variations
Remove all whitespace
s.replace(" ", "") for spaces, or "".join(s.split()) to also collapse tabs/newlines. Or use str.translate with a whitespace set.
Remove kth occurrence
Track a counter; remove only when counter matches k. O(n) time.
def remove_kth_occurrence(s: str, c: str, k: int) -> str:
result = []
count = 0
for ch in s:
if ch == c:
count += 1
if count == k:
continue
result.append(ch)
return "".join(result)
Remove all but k occurrences
Variant: keep the first k occurrences, remove the rest. Useful for deduplication-style problems.
Remove according to a predicate
Generalize to any predicate function. Most languages have stdlib filter equivalents (Python’s filter(), JavaScript’s Array.filter(), C++’s std::remove_if).
Sliding window with character removal
(LeetCode #3 — Longest Substring Without Repeating Characters) Different problem family. Sliding window pattern; remove characters from a window, not the string itself.
Common Mistakes
- Treating Python strings as mutable. Strings are immutable;
s[i] = "x"raises TypeError. Convert to list, modify, join. Or use the comprehension approach to build a new string. - Using
+=in a loop to build the result. In Python,result += chin a loop is technically O(n²) due to string immutability and reallocation. Use a list with append and join at the end. CPython has internal optimizations that sometimes hide the cost, but the pattern is fragile. - Off-by-one in the two-pointer in-place version. The write pointer must advance only when the current character is kept; the read pointer always advances. Mixing up these conditions corrupts the output.
- Returning new length but leaving stale data. If the caller uses the array after in-place removal, they should read only up to the returned length. Some implementations zero out the trailing positions; usually unnecessary.
- Confusing “remove all occurrences” with “remove first occurrence.” Read the prompt carefully.
str.replaceremoves all by default;s.replace(c, "", 1)removes only the first.
Frequently Asked Questions
What’s the expected interview answer?
List comprehension or two-pointer in-place, depending on whether the string is mutable in the interview’s language. Walk through both. Mention str.replace as the production-equivalent. The interviewer is checking whether you understand string immutability and two-pointer mechanics; clean code in either approach is sufficient.
Why is in-place removal preferred when applicable?
O(1) extra space versus O(n) for the new-string approach. For huge inputs (gigabyte-scale buffers, embedded systems with tight memory), in-place matters. For interview-sized inputs, the difference is negligible; the question is about technique, not performance.
How do I handle Unicode correctly?
Python strings are sequences of Unicode code points by default; iteration yields code points, not bytes. str.replace handles Unicode correctly. The two-pointer approach also works on lists of code points. Issues arise only at the byte level (e.g., when the string is encoded as UTF-8 bytes); avoid byte-level operations on Unicode strings.
What’s the most common follow-up?
Remove adjacent duplicates (LeetCode #1047) using a stack. The pattern teaches a different technique while sharing the “manipulate a string” theme. Other follow-ups: remove characters from a set; remove kth occurrence; sliding window for longest substring without repeats.
How does this generalize to other languages?
Java: convert to char[] for in-place; use StringBuilder for new-string approach; or String.replace. C++: use std::remove + erase idiom (the “remove-erase” pattern). JavaScript: String.replace with global flag, or Array.filter + Array.join. Go: strings.ReplaceAll. The two-pointer pattern is universal; the language-idiomatic stdlib varies.
See also: Longest Palindromic Substring • Print String Permutations • Two Sum: Find a Pair in an Array
💡Strategies for Solving This Problem
String Manipulation Basics
Simple problem that tests your understanding of strings and efficiency. Got this at Apple in 2023 as a warmup question.
The Problem
Remove all occurrences of a character from a string. For example, remove all 'a' from "banana" → "bnn".
Naive Approach: String Concatenation
Loop through string, build new string by concatenating characters that aren't the target.
Works but in languages where strings are immutable (like Java), each concatenation creates a new string object. O(n²) time.
Better: StringBuilder / Array
Use a mutable data structure. In Java, StringBuilder. In Python, list then join. In JavaScript, array then join.
O(n) time, O(n) space.
In-Place (For Mutable Strings)
If string is mutable (like char array in C), use two pointers:
- Read pointer scans forward
- Write pointer tracks where to write next keeper character
- Overwrite array in place
O(n) time, O(1) space.
Built-In Methods
Most languages have built-ins: str.replace() in JavaScript, str.replace() in Python.
Know these exist, but interviewer usually wants you to implement it yourself.
At Apple
Started with naive approach. Interviewer asked "What if the string is a million characters long?" That's when I switched to array-based solution.
Then asked: "Remove multiple characters?" Pass a set of chars to remove instead of single char.