Backspace String Compare
1. Problem Statement
Bob and Alice type two different strings. In their strings, the
character # represents a backspace.
Whenever # appears, it removes the character immediately
before it, if such a character exists.
Our task is to determine whether Bob's final text and Alice's final
text are exactly the same.
If they are the same, print:
YES
Otherwise, print:
NO
Bob and Alice type two different strings. In their strings, the
character # represents a backspace.
Whenever # appears, it removes the character immediately
before it, if such a character exists.
Our task is to determine whether Bob's final text and Alice's final text are exactly the same.
If they are the same, print:
YES
Otherwise, print:
NO
2. Understanding Backspace
Suppose Bob types:
abc#d
Let's process it step by step:
-
a → a
-
b
→ ab
-
c
→ abc
-
#
→ removes c, so we have ab
-
d
→ abd
Therefore:
abc#d
becomes:
abd
Suppose Bob types:
abc#d
Let's process it step by step:
-
a→a -
b→ab -
c→abc -
#→ removesc, so we haveab -
d→abd
Therefore:
abc#d
becomes:
abd
Another example
ab##c
Processing it:
-
a → a
-
b
→ ab
-
#
→ removes b, giving a
-
#
→ removes a, giving an empty string
-
c
→ c
Final result:
c
ab##c
Processing it:
-
a→a -
b→ab -
#→ removesb, givinga -
#→ removesa, giving an empty string -
c→c
Final result:
c
3. What Are We Comparing?
Consider:
Bob: ab#c
Alice: ac
Bob's string:
ab#c
The # removes b, so Bob ends with:
ac
Alice already has:
ac
Therefore, the answer is:
YES
Consider:
Bob: ab#c
Alice: ac
Bob's string:
ab#c
The # removes b, so Bob ends with:
ac
Alice already has:
ac
Therefore, the answer is:
YES
4. A Simple Approach
One way to solve this problem is:
-
Read Bob's string.
-
Read Alice's string.
-
Process each string and apply all backspaces.
-
Compare the resulting strings.
-
Print
YES
if they are equal; otherwise print NO.
For beginners, this approach is very easy to understand.
However, the original code uses a more efficient technique called the
two-pointer approach.
One way to solve this problem is:
- Read Bob's string.
- Read Alice's string.
- Process each string and apply all backspaces.
- Compare the resulting strings.
-
Print
YESif they are equal; otherwise printNO.
For beginners, this approach is very easy to understand.
However, the original code uses a more efficient technique called the two-pointer approach.
5. Two-Pointer Approach
Instead of creating new strings after processing all the backspaces, we
can examine both strings from right to left.
Why right to left?
Because a backspace affects the character immediately before it.
For example:
abc##d
When we start from the right side, we can easily keep track of how many
characters should be skipped.
We use:
int bobIndex = bob.size() - 1;
int aliceIndex = alice.size() - 1;
These variables point to the current characters in the two
strings.
We also maintain two counters:
int bobBackspaces = 0;
int aliceBackspaces = 0;
These counters tell us how many normal characters should be ignored
because of backspaces.
Instead of creating new strings after processing all the backspaces, we can examine both strings from right to left.
Why right to left?
Because a backspace affects the character immediately before it.
For example:
abc##d
When we start from the right side, we can easily keep track of how many characters should be skipped.
We use:
int bobIndex = bob.size() - 1;
int aliceIndex = alice.size() - 1;
These variables point to the current characters in the two strings.
We also maintain two counters:
int bobBackspaces = 0;
int aliceBackspaces = 0;
These counters tell us how many normal characters should be ignored because of backspaces.
6. How the Backspace Counter Works
Suppose we have:
abc##
Starting from the right:
#
This means one character must be deleted.
So:
bobBackspaces++;
Now we move left.
We find another:
#
So the counter becomes:
2
Now we reach:
c
Because there are two pending backspaces, c must be
skipped.
The counter becomes:
1
Then we reach:
b
It is also skipped.
The counter becomes:
0
The next character, a, is now a valid character.
This allows us to find the actual characters that remain after applying
all backspaces without physically modifying the string.
Suppose we have:
abc##
Starting from the right:
#
This means one character must be deleted.
So:
bobBackspaces++;
Now we move left.
We find another:
#
So the counter becomes:
2
Now we reach:
c
Because there are two pending backspaces, c must be
skipped.
The counter becomes:
1
Then we reach:
b
It is also skipped.
The counter becomes:
0
The next character, a, is now a valid character.
This allows us to find the actual characters that remain after applying all backspaces without physically modifying the string.
7. Important Index Correction
When using string::size(), remember that indexes start at
0.
For example:
String: a b c d
Index: 0 1 2 3
The size is 4, but the last valid index is
3.
Therefore, we should start from:
int index = str.size() - 1;
not:
int index = str.size();
The latter points one position past the end of the string and can cause
an invalid memory access.
When using string::size(), remember that indexes start at
0.
For example:
String: a b c d
Index: 0 1 2 3
The size is 4, but the last valid index is
3.
Therefore, we should start from:
int index = str.size() - 1;
not:
int index = str.size();
The latter points one position past the end of the string and can cause an invalid memory access.
8. Clean C++ Solution
#include <iostream>
#include <string>
using namespace std;
bool compareAfterBackspaces(const string& bob, const string& alice) {
int bobIndex = static_cast<int>(bob.size()) - 1;
int aliceIndex = static_cast<int>(alice.size()) - 1;
int bobBackspaces = 0;
int aliceBackspaces = 0;
while (bobIndex >= 0 || aliceIndex >= 0) {
// Find Bob's next valid character
while (bobIndex >= 0) {
if (bob[bobIndex] == '#') {
bobBackspaces++;
bobIndex--;
}
else if (bobBackspaces > 0) {
bobBackspaces--;
bobIndex--;
}
else {
break;
}
}
// Find Alice's next valid character
while (aliceIndex >= 0) {
if (alice[aliceIndex] == '#') {
aliceBackspaces++;
aliceIndex--;
}
else if (aliceBackspaces > 0) {
aliceBackspaces--;
aliceIndex--;
}
else {
break;
}
}
// Both strings have a valid character.
if (bobIndex >= 0 && aliceIndex >= 0) {
if (bob[bobIndex] != alice[aliceIndex]) {
return false;
}
}
// Only one string has a valid character.
else if (bobIndex >= 0 || aliceIndex >= 0) {
return false;
}
// Move to the next characters.
bobIndex--;
aliceIndex--;
}
return true;
}
int main() {
string bob, alice;
getline(cin, bob);
getline(cin, alice);
if (compareAfterBackspaces(bob, alice)) {
cout << "YES" << endl;
}
else {
cout << "NO" << endl;
}
return 0;
}
#include <iostream>
#include <string>
using namespace std;
bool compareAfterBackspaces(const string& bob, const string& alice) {
int bobIndex = static_cast<int>(bob.size()) - 1;
int aliceIndex = static_cast<int>(alice.size()) - 1;
int bobBackspaces = 0;
int aliceBackspaces = 0;
while (bobIndex >= 0 || aliceIndex >= 0) {
// Find Bob's next valid character
while (bobIndex >= 0) {
if (bob[bobIndex] == '#') {
bobBackspaces++;
bobIndex--;
}
else if (bobBackspaces > 0) {
bobBackspaces--;
bobIndex--;
}
else {
break;
}
}
// Find Alice's next valid character
while (aliceIndex >= 0) {
if (alice[aliceIndex] == '#') {
aliceBackspaces++;
aliceIndex--;
}
else if (aliceBackspaces > 0) {
aliceBackspaces--;
aliceIndex--;
}
else {
break;
}
}
// Both strings have a valid character.
if (bobIndex >= 0 && aliceIndex >= 0) {
if (bob[bobIndex] != alice[aliceIndex]) {
return false;
}
}
// Only one string has a valid character.
else if (bobIndex >= 0 || aliceIndex >= 0) {
return false;
}
// Move to the next characters.
bobIndex--;
aliceIndex--;
}
return true;
}
int main() {
string bob, alice;
getline(cin, bob);
getline(cin, alice);
if (compareAfterBackspaces(bob, alice)) {
cout << "YES" << endl;
}
else {
cout << "NO" << endl;
}
return 0;
}
9. Detailed Example
Suppose the input is:
ab#c
ac
Suppose the input is:
ab#c
ac
Bob
a b # c
0 1 2 3
We start from the right:
c
c is a normal character, so it is valid.
Then we compare it with Alice's last valid character:
c
They match.
Next, Bob encounters:
#
So:
bobBackspaces++;
Now one character must be skipped.
The character before # is:
b
Therefore, b is ignored.
The next valid Bob character is:
a
Alice's next valid character is also:
a
They match.
Both strings have now been completely checked.
Therefore:
YES
a b # c
0 1 2 3
We start from the right:
c
c is a normal character, so it is valid.
Then we compare it with Alice's last valid character:
c
They match.
Next, Bob encounters:
#
So:
bobBackspaces++;
Now one character must be skipped.
The character before # is:
b
Therefore, b is ignored.
The next valid Bob character is:
a
Alice's next valid character is also:
a
They match.
Both strings have now been completely checked.
Therefore:
YES
10. Why Do We Move From Right to Left?
Imagine:
hello#
The # deletes the character immediately before it:
o
When reading from the right, we encounter the # first. We
immediately know that one previous character must be ignored.
This makes it possible to process the string without creating another
string.
11. What Happens With Multiple Backspaces?
Imagine:
hello#
The # deletes the character immediately before it:
o
When reading from the right, we encounter the # first. We
immediately know that one previous character must be ignored.
This makes it possible to process the string without creating another string.
11. What Happens With Multiple Backspaces?
Consider:
abc###
Starting from the right:
#
#
#
There are three pending backspaces.
So the characters:
c
b
a
are all removed.
The final string is empty.
This is correctly handled by the counter:
backspaces++;
and later:
backspaces--;
Consider:
abc###
Starting from the right:
#
#
#
There are three pending backspaces.
So the characters:
c
b
a
are all removed.
The final string is empty.
This is correctly handled by the counter:
backspaces++;
and later:
backspaces--;
12. Important Edge Cases
Case 1: Both strings are empty
Bob: ""
Alice: ""
Both result in empty strings.
Output:
YES
Bob: ""
Alice: ""
Both result in empty strings.
Output:
YES
Case 2: Only backspaces
Bob: ###
Alice: ##
There are no characters to delete, so both become empty.
Output:
YES
Bob: ###
Alice: ##
There are no characters to delete, so both become empty.
Output:
YES
Case 3: Different final characters
Bob: abc
Alice: abd
The final characters are different.
Output:
NO
Bob: abc
Alice: abd
The final characters are different.
Output:
NO
Case 4: Different lengths but same result
Bob: a#b
Alice: b
Bob's a is removed by #, leaving:
b
Both strings are therefore equal.
Output:
YES
Bob: a#b
Alice: b
Bob's a is removed by #, leaving:
b
Both strings are therefore equal.
Output:
YES
13. Time Complexity
Let:
-
n = length of Bob's string
-
m
= length of Alice's string
Each character is processed at most once.
Therefore:
Time Complexity: O(n + m)
We only use a few integer variables, so:
Space Complexity: O(1)
This is better than creating separate processed strings when memory
usage matters.
Let:
-
n= length of Bob's string -
m= length of Alice's string
Each character is processed at most once.
Therefore:
Time Complexity: O(n + m)
We only use a few integer variables, so:
Space Complexity: O(1)
This is better than creating separate processed strings when memory usage matters.
14. Main Idea to Remember
The most important concept is:
A # creates a pending deletion for the character
immediately to its left.
When scanning from right to left:
-
# → increase the backspace counter.
-
Normal character + pending backspace → skip the character.
-
Normal character + no pending backspace → this is a character that
remains.
-
Compare the remaining characters of Bob and Alice.
If every remaining character matches, the two strings are equivalent
after applying backspaces.
The most important concept is:
A
#creates a pending deletion for the character immediately to its left.
When scanning from right to left:
-
#→ increase the backspace counter. - Normal character + pending backspace → skip the character.
- Normal character + no pending backspace → this is a character that remains.
- Compare the remaining characters of Bob and Alice.
If every remaining character matches, the two strings are equivalent after applying backspaces.
15. Final Takeaway
The problem is not really about comparing the original strings.
It is about comparing the final strings after all
# operations have been performed.
The two-pointer technique lets us find those final characters
efficiently without actually changing either input string.
For beginners, first understand the backspace simulation using a stack
or a new string. Once that idea is clear, the right-to-left two-pointer
solution becomes much easier to understand.
The problem is not really about comparing the original strings.
It is about comparing the final strings after all
# operations have been performed.
The two-pointer technique lets us find those final characters efficiently without actually changing either input string.
For beginners, first understand the backspace simulation using a stack or a new string. Once that idea is clear, the right-to-left two-pointer solution becomes much easier to understand.
0 Comments
If you have any doubts or any topics that you want to know more about them please let me know