Before attempting this problem, you should be comfortable with:
Stack - Simulating the typing process with push/pop operations for characters and backspaces
Two Pointers - Comparing strings in reverse without extra space
String Manipulation - Building and comparing processed strings
1. Stack
Intuition
The backspace character # removes the previous character, which is exactly what a stack does well. We can simulate typing each string by pushing regular characters onto a stack and popping when we see a #. After processing both strings this way, we just compare the resulting stacks.
Algorithm
Create a helper function that converts a string to its final form using a stack.
For each character in the string:
If it's # and the stack is not empty, pop from the stack.
Otherwise, push the character onto the stack.
Convert the stack to a string.
Compare the converted versions of both input strings.
classSolution:defbackspaceCompare(self, s:str, t:str)->bool:defconvert(s):
res =[]for char in s:if char =='#':if res:
res.pop()else:
res.append(char)return"".join(res)return convert(s)== convert(t)
publicclassSolution{publicbooleanbackspaceCompare(String s,String t){returnconvert(s).equals(convert(t));}privateList<Character>convert(String s){List<Character> res =newArrayList<>();for(char c : s.toCharArray()){if(c =='#'){if(!res.isEmpty()){
res.remove(res.size()-1);}}else{
res.add(c);}}return res;}}
classSolution{public:boolbackspaceCompare(string s, string t){returnconvert(s)==convert(t);}private:
string convert(const string& s){
string res ="";for(char c : s){if(c =='#'){if(!res.empty()){
res.pop_back();}}else{
res += c;}}return res;}};
classSolution{/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/backspaceCompare(s, t){constconvert=(str)=>{const res =[];for(const char of str){if(char ==='#'){if(res.length >0){
res.pop();}}else{
res.push(char);}}return res.join('');};returnconvert(s)===convert(t);}}
publicclassSolution{publicboolBackspaceCompare(string s,string t){returnConvert(s)==Convert(t);}privatestringConvert(string s){var res =newList<char>();foreach(char c in s){if(c =='#'){if(res.Count >0){
res.RemoveAt(res.Count -1);}}else{
res.Add(c);}}returnnewstring(res.ToArray());}}
funcbackspaceCompare(s string, t string)bool{
convert :=func(str string)string{
res :=[]byte{}for i :=0; i <len(str); i++{if str[i]=='#'{iflen(res)>0{
res = res[:len(res)-1]}}else{
res =append(res, str[i])}}returnstring(res)}returnconvert(s)==convert(t)}
class Solution {funbackspaceCompare(s: String, t: String): Boolean {funconvert(str: String): String {val res = mutableListOf<Char>()for(c in str){if(c =='#'){if(res.isNotEmpty()){
res.removeAt(res.size -1)}}else{
res.add(c)}}return res.joinToString("")}returnconvert(s)==convert(t)}}
classSolution{funcbackspaceCompare(_ s:String,_ t:String)->Bool{funcconvert(_ str:String)->String{var res =[Character]()for char in str {if char =="#"{if!res.isEmpty {
res.removeLast()}}else{
res.append(char)}}returnString(res)}returnconvert(s)==convert(t)}}
Time & Space Complexity
Time complexity: O(n+m)
Space complexity: O(n+m)
Where n is the length of the string s and m is the length of the string t.
2. Reverse iteration
Intuition
Instead of building the result from the beginning, we can iterate from the end. When we encounter a #, we know we need to skip the next valid character. By counting backspaces as we go backward, we can skip the right number of characters before adding one to our result. This still uses extra space for storing the result, but gives us a different perspective on the problem.
Algorithm
Create a helper function that processes a string from right to left.
Start from the last character and maintain a backspace counter.
For each character going backward:
If it's #, increment the backspace count.
Else if the backspace count is positive, decrement it (skip this character).
classSolution:defbackspaceCompare(self, s:str, t:str)->bool:defconvert(s):
res =[]
backspace =0for i inrange(len(s)-1,-1,-1):if s[i]=='#':
backspace +=1elif backspace:
backspace -=1else:
res.append(s[i])return res
return convert(s)== convert(t)
publicclassSolution{publicbooleanbackspaceCompare(String s,String t){returnconvert(s).equals(convert(t));}privateList<Character>convert(String s){List<Character> res =newArrayList<>();int backspace =0;for(int i = s.length()-1; i >=0; i--){if(s.charAt(i)=='#'){
backspace++;}elseif(backspace >0){
backspace--;}else{
res.add(s.charAt(i));}}return res;}}
classSolution{public:boolbackspaceCompare(string s, string t){returnconvert(s)==convert(t);}private:
string convert(string s){
string res;int backspace =0;for(int i = s.size()-1; i >=0; i--){if(s[i]=='#'){
backspace++;}elseif(backspace >0){
backspace--;}else{
res += s[i];}}return res;}};
classSolution{/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/backspaceCompare(s, t){constconvert=(s)=>{const res =[];let backspace =0;for(let i = s.length -1; i >=0; i--){if(s[i]==='#'){
backspace++;}elseif(backspace >0){
backspace--;}else{
res.push(s[i]);}}return res.join('');};returnconvert(s)===convert(t);}}
publicclassSolution{publicboolBackspaceCompare(string s,string t){returnConvert(s)==Convert(t);}privatestringConvert(string s){var res =newList<char>();int backspace =0;for(int i = s.Length -1; i >=0; i--){if(s[i]=='#'){
backspace++;}elseif(backspace >0){
backspace--;}else{
res.Add(s[i]);}}returnnewstring(res.ToArray());}}
funcbackspaceCompare(s string, t string)bool{
convert :=func(str string)string{
res :=[]byte{}
backspace :=0for i :=len(str)-1; i >=0; i--{if str[i]=='#'{
backspace++}elseif backspace >0{
backspace--}else{
res =append(res, str[i])}}returnstring(res)}returnconvert(s)==convert(t)}
class Solution {funbackspaceCompare(s: String, t: String): Boolean {funconvert(str: String): String {val res = mutableListOf<Char>()var backspace =0for(i in str.length -1 downTo 0){if(str[i]=='#'){
backspace++}elseif(backspace >0){
backspace--}else{
res.add(str[i])}}return res.joinToString("")}returnconvert(s)==convert(t)}}
Where n is the length of the string s and m is the length of the string t.
3. Two Pointers - I
Intuition
We can compare the strings character by character without building the full result. Starting from the end of both strings, we find the next valid character in each (skipping over characters deleted by backspaces). If at any point these characters differ, the strings are not equal. This approach uses O(1) extra space since we only track pointers and counts.
Algorithm
Initialize two pointers at the end of each string.
Create a helper function that finds the next valid character index by:
Counting backspaces encountered.
Skipping characters that would be deleted.
Returning the index of the next valid character (or -1 if none).
While either pointer is valid:
Find the next valid character in each string.
Compare them (treat out-of-bounds as empty).
If they differ, return false.
Move both pointers left.
Return true if we finish without finding a mismatch.
Where n is the length of the string s and m is the length of the string t.
4. Two Pointers - II
Intuition
This is a more compact version of the two-pointer approach. Instead of using a helper function, we inline the logic for skipping characters. The core idea remains the same: iterate backward through both strings simultaneously, skip characters that would be deleted by backspaces, and compare the remaining characters one by one.
Algorithm
Initialize pointers at the end of both strings and backspace counters for each.
Loop until both pointers are exhausted:
For each string, skip backward while there are backspaces to apply or # characters to count.
Compare the current valid characters from both strings.
If they don't match, check if both pointers are -1 (both exhausted). If not, return false.
Where n is the length of the string s and m is the length of the string t.
Common Pitfalls
Popping from Empty Stack
When processing a backspace character, you must check if the stack is non-empty before popping, since backspaces at the start of a string have no effect.
Processing Strings Left-to-Right in Two-Pointer Approach
The O(1) space two-pointer solution must iterate from right to left. Going left to right makes it impossible to know how many characters will be deleted ahead of time.
# Wrong: left-to-right doesn't work for O(1) spacefor i inrange(len(s)):# can't determine final result this way# Right: right-to-left with backspace countingfor i inrange(len(s)-1,-1,-1):