Backspace String Compare
EasyPrompt
Given two strings s and t, return true if they are equal when both are typed into empty text editors. '#' means a backspace character.
Note that after backspacing an empty text, the text will continue empty.
Example 1:
Input: s = "ab#c", t = "ad#c"
Output: true
Explanation: Both s and t become "ac".Example 2:
Input: s = "ab##", t = "c#d#"
Output: true
Explanation: Both s and t become "".Example 3:
Input: s = "a#c", t = "b"
Output: false
Explanation: s becomes "c" while t becomes "b".
Constraints:
1 <= s.length, t.length <= 200sandtonly contain lowercase letters and'#'characters.
Follow up: Can you solve it in O(n) time and O(1) space?
Approaches
2 approaches with complexity analysis and trade-offs.
This approach simulates the process of typing the characters of each string into a text editor. We can build the final resulting strings for both s and t after processing all the backspace characters. Finally, we compare the two built strings to see if they are identical.
Algorithm
- Create a helper function
build(String str). - Inside
build, initialize an emptyStringBuilder. - Iterate through each character of the input string
str. - If the character is not '#', append it to the
StringBuilder. - If the character is '#' and the
StringBuilderis not empty, remove its last character. - After the loop, return the string representation of the
StringBuilder. - In the main function, call
build(s)andbuild(t). - Compare the two resulting strings for equality and return the result.
Walkthrough
We can write a helper function that takes a string and returns its final version after applying backspaces. This function iterates through the input string and uses a StringBuilder (or a stack) to construct the result.
- When a non-backspace character is encountered, it's appended to the
StringBuilder. - When a backspace character ('#') is found, if the
StringBuilderis not empty, the last character is deleted. - After processing both
sandtwith this helper function, we get two final strings. The problem then reduces to a simple string comparison.
class Solution { public boolean backspaceCompare(String s, String t) { return build(s).equals(build(t)); } private String build(String str) { StringBuilder sb = new StringBuilder(); for (char c : str.toCharArray()) { if (c != '#') { sb.append(c); } else if (sb.length() > 0) { sb.deleteCharAt(sb.length() - 1); } } return sb.toString(); }}Complexity
Time
O(N + M), where N and M are the lengths of strings `s` and `t` respectively. We iterate through each string once to build the final strings, and then compare them.
Space
O(N + M). In the worst case (no backspaces), the `StringBuilder`s will store all characters of the original strings, where N and M are the lengths of s and t.
Trade-offs
Pros
Conceptually simple and easy to implement.
The logic directly follows the problem description.
Cons
Requires extra space proportional to the input string lengths, which does not meet the follow-up constraint of O(1) space.
To solve the problem in O(1) space, we can avoid creating new strings. Instead, we can compare the characters of s and t one by one, from right to left. Iterating from the end is beneficial because a backspace character '#' tells us to skip the next valid character to its left. This approach allows us to find the next character that would appear in the final string without actually building it.
Algorithm
- Initialize two pointers,
itos.length() - 1andjtot.length() - 1. - Initialize two skip counters,
skipSandskipT, to 0. - Loop while
i >= 0orj >= 0. - Inside the loop, find the next valid character for
s: use a nestedwhileloop that movesileft. Ifs.charAt(i)is '#', incrementskipS. Ifs.charAt(i)is a letter andskipS > 0, decrementskipS. Break when a valid character is found (letter withskipS == 0). - Similarly, find the next valid character for
tusing pointerjand counterskipT. - After finding the valid characters (or reaching the beginning of a string), compare their states:
- If both
iandjare valid indices ands.charAt(i)is not equal tot.charAt(j), returnfalse. - If one pointer is valid (
>= 0) and the other is not, it means the effective lengths are different. Returnfalse. - Decrement both
iandjto proceed to the next characters. - If the main loop completes, it means the strings are equivalent. Return
true.
Walkthrough
We use two pointers, i and j, initialized to the end of strings s and t respectively. We iterate backwards as long as there are characters to process in either string.
In each step of the main loop, we find the next valid character in s and t. A valid character is one that is not a backspace and is not skipped by a backspace.
- To find the next valid character in
s(similarly fort), we use an inner loop. We also maintain askipcounter. If we see a '#', we incrementskipand move the pointer left. If we see a non-'#' character whileskip > 0, we decrementskipand move the pointer left (as this character is 'deleted'). We stop when we find a non-'#' character withskip == 0, or when the pointer goes out of bounds. - After finding the next valid characters from both strings (or determining that one string is exhausted), we compare them.
- If the characters are different, the strings are not equivalent, and we return
false. - If one string has a valid character while the other is exhausted, they are not equivalent, so we return
false. - If both are exhausted simultaneously or the characters match, we continue by moving both pointers to the left.
- If the entire loop completes, it means the strings are equivalent, and we return
true.
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 next valid character in s while (i >= 0) { if (s.charAt(i) == '#') { skipS++; i--; } else if (skipS > 0) { skipS--; i--; } else { break; } } // Find next valid character in t while (j >= 0) { if (t.charAt(j) == '#') { skipT++; j--; } else if (skipT > 0) { skipT--; j--; } else { break; } } // If two actual characters are different if (i >= 0 && j >= 0 && s.charAt(i) != t.charAt(j)) { return false; } // If one string is empty while the other is not if ((i >= 0) != (j >= 0)) { return false; } i--; j--; } return true; }}Complexity
Time
O(N + M), where N and M are the lengths of `s` and `t`. Although there are nested loops, each character of `s` and `t` is visited at most twice, so the complexity is linear.
Space
O(1). We only use a constant amount of extra space for pointers and counters, regardless of the input size.
Trade-offs
Pros
Highly efficient in terms of space, meeting the O(1) space complexity follow-up.
Time complexity is also optimal at O(N + M).
Cons
The logic is more complex than the build-string approach, with nested loops and multiple conditions to manage.
Can be slightly harder to debug if implemented incorrectly.
Solutions
Solution
class Solution {public boolean backspaceCompare(String s, String t) { int i = s.length() - 1, j = t.length() - 1; int skip1 = 0, skip2 = 0; for (; i >= 0 || j >= 0; --i, --j) { while (i >= 0) { if (s.charAt(i) == '#') { ++skip1; --i; } else if (skip1 > 0) { --skip1; --i; } else { break; } } while (j >= 0) { if (t.charAt(j) == '#') { ++skip2; --j; } else if (skip2 > 0) { --skip2; --j; } else { break; } } if (i >= 0 && j >= 0) { if (s.charAt(i) != t.charAt(j)) { return false; } } else if (i >= 0 || j >= 0) { return false; } } return true; }}class Solution {public: bool backspaceCompare(string s, string t) { int i = s.size() - 1, j = t.size() - 1; int skip1 = 0, skip2 = 0; for (; i >= 0 || j >= 0; --i, --j) { while (i >= 0) { if (s[i] == '#') { ++skip1; --i; } else if (skip1) { --skip1; --i; } else break; } while (j >= 0) { if (t[j] == '#') { ++skip2; --j; } else if (skip2) { --skip2; --j; } else break; } if (i >= 0 && j >= 0) { if (s[i] != t[j]) return false; } else if (i >= 0 || j >= 0) return false; } return true; }};class Solution: def backspaceCompare(self, s: str, t: str) -> bool: i, j, skip1, skip2 = len(s) - 1, len(t) - 1, 0, 0 while i >= 0 or j >= 0: while i >= 0: if s[i] == '#': skip1 += 1 i -= 1 elif skip1: skip1 -= 1 i -= 1 else: break while j >= 0: if t[j] == '#': skip2 += 1 j -= 1 elif skip2: skip2 -= 1 j -= 1 else: break if i >= 0 and j >= 0: if s[i] != t[j]: return False elif i >= 0 or j >= 0: return False i, j = i - 1, j - 1 return TrueVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.