Backspace String Compare
Backspace String Compare: Reverse Two-Pointer Matching
Welcome back to codinginterview.net. If you have been following our curriculum, you are getting comfortable with hash maps, binary search, intervals, and string traversals. Now, it is time to tackle a classic string comparison problem: Backspace String Compare. This problem is a popular gauge of whether a candidate can move beyond basic buffer-building solutions and engineer space-optimized algorithms. By the end of this guide, performing backward two-pointer processing with state tracking will be second nature to you.
1. Understanding the Problem
Given two strings s and t, return true if they are equal when both are typed into empty text editors. The character '#' represents a backspace character.
Note that after backspacing an empty text, the text will continue to remain empty.
| Input String s | Input String t | Processed s | Processed t | Output |
|---|---|---|---|---|
| ab#c | ad#c | ac | ac | true |
| ab## | c#d# | "" | "" | true |
| a#c | b | c | b | false |
For example, "ab#c" turns into "ac" because the '#' erases the character 'b' right before it. Similarly, "ab##" becomes an empty string because the two consecutive backspaces remove both 'b' and 'a'.
The Core Challenge:
While dynamic character deletion can be simulated easily with auxiliary data structures, doing so requires extra memory. The true test in an interview is handling backspace cancellations on the fly.
2. The Naive Approach: Stack Simulation
The most intuitive way to handle the problem is to build the final string for both s and t independently using a Stack or dynamic array, then compare the two processed strings character by character.
Trade-off Analysis
While this strategy is easy to reason about, it incurs additional overhead:
- Time Complexity: O(N + M) — Linear scan through both strings of lengths N and M.
- Space Complexity: O(N + M) — Creating dynamic stacks allocates extra memory proportional to string length.
3. The Optimal Approach: Reverse Two-Pointer Traversal (O(N) Time, O(1) Space)
To pass a senior-level technical interview, we want to achieve a clean single-pass O(N + M) time complexity with strictly O(1) auxiliary space.
Instead of reading left to right (where a backspace deletes a character we already passed), we iterate right to left from the end of both strings. When moving backward:
- A backspace character
'#'tells us to skip the next valid character we encounter to its left. - We maintain a count of active backspaces for each string to know how many non-
'#'characters to ignore. - Once both pointers land on their next surviving character, we compare them. If they match, we continue backward; if not, we return
false.
This single observation completely unifies the logic, seamlessly handling multiple backspaces and empty states without any string allocations!
4. The Logic Step-by-Step
- Initialize pointers
i = len(s) - 1andj = len(t) - 1, along withskip_s = 0andskip_t = 0. - Outer Loop: While
i >= 0orj >= 0:- Find Valid Char in s: Loop backward using
i. Incrementskip_son'#', or decrementskip_sand move left ifskip_s > 0. Stop when reaching a valid character. - Find Valid Char in t: Loop backward using
j. Incrementskip_ton'#', or decrementskip_tand move left ifskip_t > 0. Stop when reaching a valid character. - Compare Characters:
- If both
iandjare valid indices, check ifs[i] == t[j]. If not, returnfalse. - If one string has a remaining valid character while the other string is exhausted, return
false.
- If both
- Find Valid Char in s: Loop backward using
- Return
trueif pointers finish traversing both strings without mismatches.
5. Complexity Analysis
- Time Complexity: O(N + M) — We traverse each character of string
s(length N) and stringt(length M) at most once. - Space Complexity: O(1) — Memory usage remains strictly constant as we only store a few pointer and count variables.
6. Code Implementations
Expand the sections below to see the optimal O(1) space "Reverse Two-Pointer Traversal" implementations across different languages.
View Python Solution
class Solution:
def backspaceCompare(self, s: str, t: str) -> bool:
i, j = len(s) - 1, len(t) - 1
skip_s, skip_t = 0, 0
while i >= 0 or j >= 0:
# Find index of next valid character in s
while i >= 0:
if s[i] == '#':
skip_s += 1
i -= 1
elif skip_s > 0:
skip_s -= 1
i -= 1
else:
break
# Find index of next valid character in t
while j >= 0:
if t[j] == '#':
skip_t += 1
j -= 1
elif skip_t > 0:
skip_t -= 1
j -= 1
else:
break
# Compare valid characters
if i >= 0 and j >= 0:
if s[i] != t[j]:
return False
# If one string ran out of characters while the other didn't
elif (i >= 0) != (j >= 0):
return False
i -= 1
j -= 1
return True
View Java Solution
class Solution {
public boolean backspaceCompare(String s, String t) {
int i = s.length() - 1;
int j = t.length() - 1;
int skipS = 0;
int skipT = 0;
while (i >= 0 || j >= 0) {
// Find index of next valid character in s
while (i >= 0) {
if (s.charAt(i) == '#') {
skipS++;
i--;
} else if (skipS > 0) {
skipS--;
i--;
} else {
break;
}
}
// Find index of next valid character in t
while (j >= 0) {
if (t.charAt(j) == '#') {
skipT++;
j--;
} else if (skipT > 0) {
skipT--;
j--;
} else {
break;
}
}
// Compare valid characters
if (i >= 0 && j >= 0) {
if (s.charAt(i) != t.charAt(j)) {
return false;
}
} else if ((i >= 0) != (j >= 0)) {
return false;
}
i--;
j--;
}
return true;
}
}
View C++ Solution
#include <string>
class Solution {
public:
bool backspaceCompare(std::string s, std::string t) {
int i = s.length() - 1;
int j = t.length() - 1;
int skipS = 0;
int skipT = 0;
while (i >= 0 || j >= 0) {
// Find index of next valid character in s
while (i >= 0) {
if (s[i] == '#') {
skipS++;
i--;
} else if (skipS > 0) {
skipS--;
i--;
} else {
break;
}
}
// Find index of next valid character in t
while (j >= 0) {
if (t[j] == '#') {
skipT++;
j--;
} else if (skipT > 0) {
skipT--;
j--;
} else {
break;
}
}
// Compare valid characters
if (i >= 0 && j >= 0) {
if (s[i] != t[j]) {
return false;
}
} else if ((i >= 0) != (j >= 0)) {
return false;
}
i--;
j--;
}
return true;
}
};
7. Conclusion: You Are Ready
Congratulations, you have just mastered the "Backspace String Compare" pattern! The key takeaway from this problem is discovering the unifying directional insight: when backspace actions invalidate past elements, reading backward converts unknown future operations into manageable skip counts. By leveraging two reverse pointers, you converted what could have required auxiliary data structures into an optimal O(1) space solution. Keep this reverse two-pointer technique in your mental toolkit, as it is a crucial pattern for stream processing and string editing interview questions!