LeetCode 844简单栈/双指针
比较含退格的字符串 图解题解
这道题到底在问什么
给定字符串 s 和 t,# 表示退格键。判断两者执行完所有退格后是否相等。串只含小写字母和 #,可能为空。
- 输入
- s="ab#c", t="ad#c"
- 输出
- true(都变成 "ac")
- 输入
- s="ab##", t="c#d#"
- 输出
- true(都变成空串)
- 输入
- s="a#c", t="b"
- 输出
- false("c" 与 "b")
最优解:一步一步想明白
- 3记住「从右往左 + skip 计数,找有效字符两两比」。为什么从右往左?因为退格只影响它左边的字符,从右扫才能当场算清谁被删。
- 4先看 s。指针从最右第 7 位起步,带一个 skip=0。下面一格一格往左走,边走边算哪些字符真正留下来。
- 5t 也一样,指针落在最右第 7 位、skip=0。两个串各自找「下一个有效字符」,再把这一对拿来比。
- 6s 的第 7 位是 "e",此刻 skip=0、没有欠账,它就是真正留下的有效字符,标绿。s 当前化简成 "e"。
- 7t 的第 7 位是 "e",此刻 skip=0、没有欠账,它就是真正留下的有效字符,标绿。t 当前化简成 "e"。
- 8把这一对有效字符摆出来比:s 是 "e"、t 是 "e"。一样,接着找下一对。
- 9s 的第 6 位是 "d",此刻 skip=0、没有欠账,它就是真正留下的有效字符,标绿。s 当前化简成 "de"。
- 10t 的第 6 位是 "d",此刻 skip=0、没有欠账,它就是真正留下的有效字符,标绿。t 当前化简成 "de"。
- 11把这一对有效字符摆出来比:s 是 "d"、t 是 "d"。一样,接着找下一对。
- 12s 的第 5 位是退格键 #,欠一次删除,skip 加到 1。# 本身不是字符,继续往左。
- 13s 的第 4 位是 "c",可前面还欠着退格(skip 从 1 减到 0),所以它被删掉、灰掉跳过。
- 14s 的第 3 位是退格键 #,欠一次删除,skip 加到 1。# 本身不是字符,继续往左。
- 15s 的第 2 位是 "b",可前面还欠着退格(skip 从 1 减到 0),所以它被删掉、灰掉跳过。
- 16s 的第 1 位是 "a",此刻 skip=0、没有欠账,它就是真正留下的有效字符,标绿。s 当前化简成 "ade"。
- 17t 的第 5 位是退格键 #,欠一次删除,skip 加到 1。# 本身不是字符,继续往左。
- 18t 的第 4 位是退格键 #,欠一次删除,skip 加到 2。# 本身不是字符,继续往左。
- 19t 的第 3 位是 "z",可前面还欠着退格(skip 从 2 减到 1),所以它被删掉、灰掉跳过。
- 20t 的第 2 位是 "b",可前面还欠着退格(skip 从 1 减到 0),所以它被删掉、灰掉跳过。
- 21t 的第 1 位是 "a",此刻 skip=0、没有欠账,它就是真正留下的有效字符,标绿。t 当前化简成 "ade"。
- 22把这一对有效字符摆出来比:s 是 "a"、t 是 "a"。一样,接着找下一对。
- 23s 的第 0 位是 "x",此刻 skip=0、没有欠账,它就是真正留下的有效字符,标绿。s 当前化简成 "xade"。
- 24t 的第 0 位是 "x",此刻 skip=0、没有欠账,它就是真正留下的有效字符,标绿。t 当前化简成 "xade"。
- 25把这一对有效字符摆出来比:s 是 "x"、t 是 "x"。一样,接着找下一对。
- 26两个串从右扫到头,逐对有效字符全都对得上,s 化简成 "xade"、t 化简成 "xade",完全一样,返回 true。全程只用了两个指针和两个 skip 计数,没有另开字符串,空间 O(1)。
⚠️ 容易写错的地方
✗ 错:从左往右处理退格
✓ 对:一定从右往左 + skip 计数
退格删的是它左边的字符,只有从右扫才能当场知道当前字符会不会被后面的 # 删掉
✗ 错:把 # 也当有效字符比较
✓ 对:# 只加 skip、不参与比较
# 是操作不是字符,跳过它本身、用 skip 记账
✗ 错:一个串扫完就收工
✓ 对:主循环要 i >= 0 或 j >= 0
两边有效字符个数可能不同,必须都扫完;一边空一边还有字符,就该判不等
完整代码(Python / C++ / Java)
Python
class Solution:
def backspaceCompare(self, s: str, t: str) -> bool:
def next_char(x, i):
skip = 0
while i >= 0:
if x[i] == '#':
skip += 1
elif skip:
skip -= 1
else:
return x[i], i - 1
i -= 1
return '', -1
i, j = len(s) - 1, len(t) - 1
while i >= 0 or j >= 0:
a, i = next_char(s, i)
b, j = next_char(t, j)
if a != b:
return False
return TrueC++
#include <string>
using namespace std;
class Solution {
public:
pair<char,int> nextChar(const string& s, int i) {
int skip = 0;
while (i >= 0) {
if (s[i] == '#') skip++;
else if (skip) skip--;
else return {s[i], i - 1};
i--;
}
return {'\0', -1};
}
bool backspaceCompare(string s, string t) {
int i = s.size() - 1, j = t.size() - 1;
while (i >= 0 || j >= 0) {
auto a = nextChar(s, i);
auto b = nextChar(t, j);
if (a.first != b.first) return false;
i = a.second; j = b.second;
}
return true;
}
};Java
import java.util.*;
class Solution {
private int next(String s, int i, char[] out) {
int skip = 0;
while (i >= 0) {
char c = s.charAt(i);
if (c == '#') skip++;
else if (skip > 0) skip--;
else { out[0] = c; return i - 1; }
i--;
}
out[0] = 0;
return -1;
}
public boolean backspaceCompare(String s, String t) {
int i = s.length() - 1, j = t.length() - 1;
char[] a = new char[1], b = new char[1];
while (i >= 0 || j >= 0) {
i = next(s, i, a);
j = next(t, j, b);
if (a[0] != b[0]) return false;
}
return true;
}
}复杂度
时间
O(n + m)
n、m 是两串长度,两个指针各把自己的串从右到左扫一遍,每个字符只看一次
空间
O(1)
只用两个下标和两个 skip 计数器,不另开字符串或栈
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 比较含退格的字符串 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
还有别的解法吗?+
最直观的是用栈:遍历每个串,普通字符压栈、遇 # 就弹栈,最后比较两个栈。逻辑简单但要 O(n+m) 额外空间。逆向双指针是它的 O(1) 空间优化版,面试里能写出双指针版更加分。
为什么双指针能做到 O(1) 空间?+
因为不真的把结果串建出来,只是「按需」从右往左各找下一个有效字符当场比对。每个串维护一个下标和一个 skip 整数就够,不需要存下化简后的串。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 比较含退格的字符串 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。