The problem. Two strings s and t are typed into empty text editors, where # means backspace. Return true if both editors end up showing the same text. A backspace on an empty editor does nothing.
s = "ab#c", t = "ad#c" -> true (both "ac")
s = "ab##", t = "c#d#" -> true (both empty)
s = "a#c", t = "b" -> false ("c" vs "b")A stack fits whenever the newest character is the one that can be cancelled, matched or undone: backspaces erase the last letter typed, a ) closes the most recent (, a letter cancels an equal letter right before it. Push characters as you read them; when one cancels, pop the top. Whatever is left on the stack is the result.
The Stack course covers the basics, Valid Parentheses and Decode String. This chapter applies the same idea to three string-editing questions, from a direct use to one where the stack stores indexes to delete.
Build the final text of each string with a stack (an array, a list, a StringBuilder or a std::string used as a stack), then compare the two.
Letters are pushed; each # pops the most recent one. Try a backspace on an empty editor.
function backspaceCompare(s, t) { const build = (str) => { const stack = []; for (const ch of str) { if (ch === "#") stack.pop(); // backspace: remove the last character typed else stack.push(ch); // (popping an empty stack does nothing) } return stack.join(""); }; return build(s) === build(t);}Time O(n + m), space O(n + m). Every character is pushed or popped once, and the two stacks can hold every letter.
The classic follow-up asks for O(1) extra space. Read both strings backwards: then a # is seen before the letter it erases, so just count pending backspaces and skip that many letters. Compare the surviving characters one pair at a time.
function backspaceCompare(s, t) { let i = s.length - 1, j = t.length - 1; let skipS = 0, skipT = 0; // backspaces still to apply while (i >= 0 || j >= 0) { while (i >= 0) { // find the next character of s that survives if (s[i] === "#") { skipS++; i--; } else if (skipS > 0) { skipS--; i--; } else break; } while (j >= 0) { // ...and the next one of t if (t[j] === "#") { skipT++; j--; } else if (skipT > 0) { skipT--; j--; } else break; } if (i >= 0 && j >= 0 && s[i] !== t[j]) return false; // different surviving characters if ((i >= 0) !== (j >= 0)) return false; // one string has more left i--; j--; } return true;}Time O(n + m), space O(1). Each pointer only moves left, so every character is passed once.
Lead with the stack — it states the problem directly. Then offer the backwards scan for O(1) space: reading in reverse turns "undo the past" into "skip the future", which needs only a counter.
"#a" is just "a"."ab##" and "c#d#" are both empty: true."abc###d" is "d"."xy#z" and "xz": true."../" pops a folder, "x/" pushes one: count the depth that's left./, push folder names, pop on .., ignore ..