Facebook Pixel

844. Backspace String Compare

EasyStackTwo PointersStringSimulation
Leetcode Link

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 and j starting from the end of strings s and t
  • Counters skip1 and skip2 to track how many characters to skip due to backspaces

For each string, when traversing from right to left:

  1. If a '#' is found, increment the skip counter
  2. If a regular character is found and skip counter > 0, skip it and decrement the counter
  3. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 and j to the last indices of strings s and t respectively
  • Initialize skip1 and skip2 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 '#', increment skip1 and move left
  • If we encounter a regular character but skip1 > 0, this character will be deleted, so decrement skip1 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 Evaluator

Example Walkthrough

Let's walk through the algorithm with s = "ab#c" and t = "ad#c".

Initial Setup:

  • s = "ab#c" with i = 3 (pointing at 'c')
  • t = "ad#c" with j = 3 (pointing at 'c')
  • skip1 = 0, skip2 = 0

Iteration 1:

  • Finding next valid character in s:
    • s[3] = 'c', not '#' and skip1 = 0, so 'c' is valid
  • Finding next valid character in t:
    • t[3] = 'c', not '#' and skip2 = 0, so 'c' is valid
  • Compare: s[3] = 'c' equals t[3] = 'c'
  • Move pointers: i = 2, j = 2

Iteration 2:

  • Finding next valid character in s:
    • s[2] = '#', increment skip1 = 1, move i = 1
    • s[1] = 'b', skip1 = 1 > 0, so skip 'b', decrement skip1 = 0, move i = 0
    • s[0] = 'a', not '#' and skip1 = 0, so 'a' is valid
  • Finding next valid character in t:
    • t[2] = '#', increment skip2 = 1, move j = 1
    • t[1] = 'd', skip2 = 1 > 0, so skip 'd', decrement skip2 = 0, move j = 0
    • t[0] = 'a', not '#' and skip2 = 0, so 'a' is valid
  • Compare: s[0] = 'a' equals t[0] = 'a'
  • Move pointers: i = -1, j = -1

Iteration 3:

  • Both i < 0 and j < 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:

  1. If both pointers are valid (i >= 0 and j >= 0), compare characters
  2. If only one is valid (i >= 0 or j >= 0), return False
  3. 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.

Discover Your Strengths and Weaknesses: Take Our 3-Minute Quiz to Tailor Your Study Plan:

Which of the following is a min heap?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More