844. Backspace String Compare
Problem Description
You're given two strings s
and t
that represent text typed into text editors. The special character '#'
represents a backspace operation that deletes the previous character. Your task is to determine if both strings result in the same final text after processing all the backspace operations.
When a backspace '#'
is encountered:
- It deletes the character immediately before it (if one exists)
- If there's no character to delete (empty text), the backspace has no effect
For example:
"ab#c"
becomes"ac"
(the 'b' is deleted by the '#')"ab##"
becomes""
(both 'b' and 'a' are deleted)"#a#"
becomes""
(first '#' has no effect, second '#' deletes 'a')
The solution uses a two-pointer approach that processes both strings from right to left. This avoids building the final strings explicitly, achieving O(1) space complexity.
The algorithm maintains:
- Pointers
i
andj
starting from the end of stringss
andt
- Counters
skip1
andskip2
to track how many characters to skip due to backspaces
For each string, when traversing from right to left:
- If a
'#'
is found, increment the skip counter - If a regular character is found and skip counter > 0, skip it and decrement the counter
- If a regular character is found and skip counter = 0, this character remains in the final string
The algorithm compares characters that remain in the final strings and returns true
only if all remaining characters match in both strings.
Intuition
The key insight is recognizing that backspace operations affect characters that come before them, not after. This means when we process the string from left to right, we can't immediately determine which characters will remain in the final string - we need to look ahead to see if there are backspaces coming.
However, if we process the string from right to left, we can immediately know how many characters to skip. When we encounter a '#'
, we know it will delete one character to its left. By maintaining a counter of pending deletions, we can skip the appropriate number of characters as we move leftward.
Why not just build the final strings and compare them? While that would work, it requires O(n) extra space. The challenge is to solve this more efficiently.
The two-pointer technique emerges naturally from this right-to-left processing idea. Since we need to compare the final results of both strings, we can process them simultaneously. At each step:
- We find the next "surviving" character in string
s
(one that won't be deleted by backspaces) - We find the next "surviving" character in string
t
- We compare these characters
The beauty of this approach is that we never need to store the final strings. We're essentially simulating what the final strings would look like and comparing them character by character on the fly.
The skip counters (skip1
and skip2
) act like a stack mechanism without actually using a stack. When we see a '#'
, we increment the counter (like pushing to a stack). When we see a regular character and the counter is positive, we skip it and decrement the counter (like popping from a stack). This elegantly handles cases where multiple backspaces occur consecutively, such as "abc###"
.
Learn more about Stack and Two Pointers patterns.
Solution Approach
The implementation uses a two-pointer approach with counters to track backspace operations. Here's how the algorithm works:
Initialization:
- Set pointers
i
andj
to the last indices of stringss
andt
respectively - Initialize
skip1
andskip2
to 0 to track pending backspaces for each string
Main Loop:
The algorithm continues while at least one pointer hasn't reached the beginning of its string (i >= 0 or j >= 0
).
Finding the next valid character in s
:
while i >= 0: if s[i] == '#': skip1 += 1 i -= 1 elif skip1: skip1 -= 1 i -= 1 else: break
This inner loop processes characters from right to left:
- If we encounter a
'#'
, incrementskip1
and move left - If we encounter a regular character but
skip1 > 0
, this character will be deleted, so decrementskip1
and move left - If we encounter a regular character and
skip1 = 0
, this character survives - break the loop
Finding the next valid character in t
:
The same logic applies to string t
using pointer j
and counter skip2
.
Character Comparison: After finding the next valid characters in both strings:
if i >= 0 and j >= 0: if s[i] != t[j]: return False elif i >= 0 or j >= 0: return False
- If both pointers are valid, compare the characters. Return
False
if they don't match - If only one pointer is valid (one string has more surviving characters), return
False
- If both pointers are invalid (both have been fully processed), continue
Move to Next Characters:
i, j = i - 1, j - 1
Move both pointers left to check the next pair of surviving characters.
Final Return:
If the loop completes without finding mismatches, both strings produce the same result, so return True
.
Time Complexity: O(n + m) where n and m are the lengths of strings s
and t
Space Complexity: O(1) as we only use a constant amount of extra space
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the algorithm with s = "ab#c"
and t = "ad#c"
.
Initial Setup:
s = "ab#c"
withi = 3
(pointing at 'c')t = "ad#c"
withj = 3
(pointing at 'c')skip1 = 0
,skip2 = 0
Iteration 1:
- Finding next valid character in
s
:s[3] = 'c'
, not '#' andskip1 = 0
, so 'c' is valid
- Finding next valid character in
t
:t[3] = 'c'
, not '#' andskip2 = 0
, so 'c' is valid
- Compare:
s[3] = 'c'
equalst[3] = 'c'
✓ - Move pointers:
i = 2
,j = 2
Iteration 2:
- Finding next valid character in
s
:s[2] = '#'
, incrementskip1 = 1
, movei = 1
s[1] = 'b'
,skip1 = 1
> 0, so skip 'b', decrementskip1 = 0
, movei = 0
s[0] = 'a'
, not '#' andskip1 = 0
, so 'a' is valid
- Finding next valid character in
t
:t[2] = '#'
, incrementskip2 = 1
, movej = 1
t[1] = 'd'
,skip2 = 1
> 0, so skip 'd', decrementskip2 = 0
, movej = 0
t[0] = 'a'
, not '#' andskip2 = 0
, so 'a' is valid
- Compare:
s[0] = 'a'
equalst[0] = 'a'
✓ - Move pointers:
i = -1
,j = -1
Iteration 3:
- Both
i < 0
andj < 0
, so both strings are fully processed - Exit loop and return
True
The algorithm correctly determines that both strings produce "ac" after processing backspaces.
Let's trace through the algorithm with `s = "ab#c"` and `t = "ad#c"`.
**Initial State:**
- String `s = "ab#c"`, pointer `i = 3` (at 'c')
- String `t = "ad#c"`, pointer `j = 3` (at 'c')
- Backspace counters: `skip1 = 0`, `skip2 = 0`
**First Comparison:**
- In `s`: Character at index 3 is 'c' (regular character, `skip1 = 0`) → Valid character 'c'
- In `t`: Character at index 3 is 'c' (regular character, `skip2 = 0`) → Valid character 'c'
- Compare: 'c' == 'c' ✓
- Move both pointers left: `i = 2`, `j = 2`
**Second Comparison:**
- In `s`:
- Index 2 has '#' → Increment `skip1 = 1`, move to index 1
- Index 1 has 'b' with `skip1 = 1` → Skip 'b', decrement `skip1 = 0`, move to index 0
- Index 0 has 'a' with `skip1 = 0` → Valid character 'a'
- In `t`:
- Index 2 has '#' → Increment `skip2 = 1`, move to index 1
- Index 1 has 'd' with `skip2 = 1` → Skip 'd', decrement `skip2 = 0`, move to index 0
- Index 0 has 'a' with `skip2 = 0` → Valid character 'a'
- Compare: 'a' == 'a' ✓
- Move both pointers left: `i = -1`, `j = -1`
**Termination:**
- Both pointers are now at -1 (before the start of their strings)
- Both strings have been fully processed with all characters matching
- Return `True`
The algorithm correctly identifies that both strings result in "ac" after processing backspaces.
Solution Implementation
1class Solution:
2 def backspaceCompare(self, s: str, t: str) -> bool:
3 """
4 Compare two strings after processing backspace characters ('#').
5 Uses two pointers traversing from the end of each string.
6 Time: O(n + m), Space: O(1) where n and m are lengths of s and t.
7 """
8 # Initialize pointers at the end of both strings
9 i = len(s) - 1
10 j = len(t) - 1
11 skip_s = 0 # Count of backspaces to skip in string s
12 skip_t = 0 # Count of backspaces to skip in string t
13
14 # Process both strings from right to left
15 while i >= 0 or j >= 0:
16 # Find the next valid character in string s (after processing backspaces)
17 while i >= 0:
18 if s[i] == '#':
19 # Found a backspace, increment skip counter
20 skip_s += 1
21 i -= 1
22 elif skip_s > 0:
23 # Skip this character due to backspace
24 skip_s -= 1
25 i -= 1
26 else:
27 # Found a valid character that won't be deleted
28 break
29
30 # Find the next valid character in string t (after processing backspaces)
31 while j >= 0:
32 if t[j] == '#':
33 # Found a backspace, increment skip counter
34 skip_t += 1
35 j -= 1
36 elif skip_t > 0:
37 # Skip this character due to backspace
38 skip_t -= 1
39 j -= 1
40 else:
41 # Found a valid character that won't be deleted
42 break
43
44 # Compare the current valid characters
45 if i >= 0 and j >= 0:
46 # Both strings have valid characters to compare
47 if s[i] != t[j]:
48 return False
49 elif i >= 0 or j >= 0:
50 # One string has a character while the other doesn't
51 return False
52
53 # Move to the next characters
54 i -= 1
55 j -= 1
56
57 # All characters matched successfully
58 return True
59
1class Solution {
2 public boolean backspaceCompare(String s, String t) {
3 // Initialize pointers to traverse both strings from the end
4 int sPointer = s.length() - 1;
5 int tPointer = t.length() - 1;
6
7 // Track the number of backspaces to skip for each string
8 int sBackspaceCount = 0;
9 int tBackspaceCount = 0;
10
11 // Continue while at least one string has characters to process
12 while (sPointer >= 0 || tPointer >= 0) {
13
14 // Process string s: Skip characters affected by backspaces
15 while (sPointer >= 0) {
16 if (s.charAt(sPointer) == '#') {
17 // Found a backspace, increment counter
18 sBackspaceCount++;
19 sPointer--;
20 } else if (sBackspaceCount > 0) {
21 // Skip this character due to backspace
22 sBackspaceCount--;
23 sPointer--;
24 } else {
25 // Found a valid character to compare
26 break;
27 }
28 }
29
30 // Process string t: Skip characters affected by backspaces
31 while (tPointer >= 0) {
32 if (t.charAt(tPointer) == '#') {
33 // Found a backspace, increment counter
34 tBackspaceCount++;
35 tPointer--;
36 } else if (tBackspaceCount > 0) {
37 // Skip this character due to backspace
38 tBackspaceCount--;
39 tPointer--;
40 } else {
41 // Found a valid character to compare
42 break;
43 }
44 }
45
46 // Compare the current valid characters
47 if (sPointer >= 0 && tPointer >= 0) {
48 // Both strings have valid characters to compare
49 if (s.charAt(sPointer) != t.charAt(tPointer)) {
50 return false;
51 }
52 } else if (sPointer >= 0 || tPointer >= 0) {
53 // One string has a character while the other doesn't
54 return false;
55 }
56
57 // Move to the next characters
58 sPointer--;
59 tPointer--;
60 }
61
62 // Both strings have been fully processed and match
63 return true;
64 }
65}
66
1class Solution {
2public:
3 bool backspaceCompare(string s, string t) {
4 // Use two pointers starting from the end of each string
5 int sIndex = s.size() - 1;
6 int tIndex = t.size() - 1;
7
8 // Track number of backspaces to skip for each string
9 int sBackspaceCount = 0;
10 int tBackspaceCount = 0;
11
12 // Process both strings from right to left
13 while (sIndex >= 0 || tIndex >= 0) {
14 // Process string s: skip characters affected by backspaces
15 while (sIndex >= 0) {
16 if (s[sIndex] == '#') {
17 // Found a backspace, increment counter
18 sBackspaceCount++;
19 sIndex--;
20 } else if (sBackspaceCount > 0) {
21 // Skip this character due to backspace
22 sBackspaceCount--;
23 sIndex--;
24 } else {
25 // Found a valid character to compare
26 break;
27 }
28 }
29
30 // Process string t: skip characters affected by backspaces
31 while (tIndex >= 0) {
32 if (t[tIndex] == '#') {
33 // Found a backspace, increment counter
34 tBackspaceCount++;
35 tIndex--;
36 } else if (tBackspaceCount > 0) {
37 // Skip this character due to backspace
38 tBackspaceCount--;
39 tIndex--;
40 } else {
41 // Found a valid character to compare
42 break;
43 }
44 }
45
46 // Compare the current valid characters
47 if (sIndex >= 0 && tIndex >= 0) {
48 // Both strings have valid characters to compare
49 if (s[sIndex] != t[tIndex]) {
50 return false;
51 }
52 } else if (sIndex >= 0 || tIndex >= 0) {
53 // One string has characters while the other doesn't
54 return false;
55 }
56
57 // Move to the next characters
58 sIndex--;
59 tIndex--;
60 }
61
62 // All characters matched successfully
63 return true;
64 }
65};
66
1/**
2 * Compares two strings after processing backspace characters (#)
3 * Uses two-pointer technique scanning from right to left
4 * Time: O(n + m), Space: O(1)
5 */
6function backspaceCompare(s: string, t: string): boolean {
7 let sPointer: number = s.length - 1;
8 let tPointer: number = t.length - 1;
9
10 // Continue while either string has characters to process
11 while (sPointer >= 0 || tPointer >= 0) {
12 // Process backspaces in string s
13 let backspaceCount: number = 0;
14 while (sPointer >= 0) {
15 if (s[sPointer] === '#') {
16 // Found a backspace, increment counter
17 backspaceCount++;
18 } else if (backspaceCount !== 0) {
19 // Skip this character due to backspace
20 backspaceCount--;
21 } else {
22 // Found a valid character to compare
23 break;
24 }
25 sPointer--;
26 }
27
28 // Process backspaces in string t
29 backspaceCount = 0;
30 while (tPointer >= 0) {
31 if (t[tPointer] === '#') {
32 // Found a backspace, increment counter
33 backspaceCount++;
34 } else if (backspaceCount !== 0) {
35 // Skip this character due to backspace
36 backspaceCount--;
37 } else {
38 // Found a valid character to compare
39 break;
40 }
41 tPointer--;
42 }
43
44 // Compare the current valid characters
45 if (s[sPointer] !== t[tPointer]) {
46 return false;
47 }
48
49 // Move to the next characters
50 sPointer--;
51 tPointer--;
52 }
53
54 // All characters matched successfully
55 return true;
56}
57
Time and Space Complexity
Time Complexity: O(n + m)
where n
is the length of string s
and m
is the length of string t
.
The algorithm uses two pointers traversing from the end of each string toward the beginning. In the worst case, each character in both strings is visited at most twice:
- Once when encountering a
#
character (incrementing skip counter) - Once when being skipped or compared
The outer while loop continues as long as either pointer hasn't reached the beginning of its respective string. Each iteration either processes backspaces or compares characters, moving at least one pointer backward. Therefore, the total number of operations is proportional to the sum of the lengths of both strings.
Space Complexity: O(1)
The algorithm uses only a constant amount of extra space:
- Four integer variables:
i
,j
,skip1
,skip2
- These variables don't depend on the input size
The solution processes the strings in-place using the two-pointer technique without creating any additional data structures or modified copies of the input strings, achieving constant space complexity.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Incorrect Skip Counter Management
Issue: A frequent mistake is not properly handling the skip counter when encountering consecutive backspaces. Developers might reset the skip counter prematurely or fail to accumulate multiple backspaces.
Incorrect Implementation:
# Wrong: Resetting skip counter incorrectly while i >= 0: if s[i] == '#': skip_s = 1 # Wrong! Should be skip_s += 1 i -= 1 elif skip_s > 0: skip_s -= 1 i -= 1 else: break
Why it fails: For input like "ab###"
, this would only delete one character instead of three. The skip counter must accumulate to handle multiple consecutive backspaces.
Solution: Always increment the skip counter (skip_s += 1
) when encountering a backspace, never set it to a fixed value.
Pitfall 2: Boundary Check After Inner Loops
Issue: Forgetting to check if both pointers are still valid after finding the next valid characters can lead to incorrect comparisons or index out of bounds errors.
Incorrect Implementation:
# Wrong: Comparing without checking bounds while i >= 0 or j >= 0: # ... find next valid characters ... # Wrong! Might access invalid indices if s[i] != t[j]: return False i -= 1 j -= 1
Why it fails: After processing backspaces, one pointer might be at -1 while the other is still valid. Directly comparing s[i]
and t[j]
would cause an error or incorrect comparison.
Solution: Always check both conditions:
- If both pointers are valid (
i >= 0 and j >= 0
), compare characters - If only one is valid (
i >= 0 or j >= 0
), return False - If neither is valid, continue (both strings are exhausted at this position)
Pitfall 3: Not Processing All Backspaces Before Comparison
Issue: Breaking out of the inner loop too early without fully processing all pending backspaces.
Incorrect Implementation:
# Wrong: Breaking immediately when finding a non-backspace character while i >= 0: if s[i] != '#': break # Wrong! Doesn't check if skip_s > 0 skip_s += 1 i -= 1
Why it fails: For input like "abc##"
, this would incorrectly identify 'c' as a valid character when it should be deleted by one of the backspaces.
Solution: The inner loop must continue until finding a character that won't be deleted (when skip_s == 0
and character is not '#'`), not just any non-backspace character.
Which of the following is a min heap?
Recommended Readings
Stack Intro Imagine you have a pile of books on your desk If you want to add a new book you place it on top If you want to read a book you take it from the top And if you simply want to see which book is on the top you
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!