844. 比较含退格的字符串
题目
给定 S 和 T 两个字符串,当它们分别被输入到空白的文本编辑器后,判断二者是否相等,并返回结果。 # 代表退格字符。
注意:如果对空文本输入退格字符,文本继续为空。
示例 1:
输入:S = “ab#c”, T = “ad#c”
输出:true
解释:S 和 T 都会变成 “ac”。
示例 2:
输入:S = “ab##”, T = “c#d#”
输出:true
解释:S 和 T 都会变成 “”。
示例 3:
输入:S = “a##c”, T = “#a#c”
输出:true
解释:S 和 T 都会变成 “c”。
示例 4:
输入:S = “a#c”, T = “b”
输出:false
解释:S 会变成 “c”,但 T 仍然是 “b”。
进阶:
你可以用 O(N) 的时间复杂度和 O(1) 的空间复杂度解决该问题吗?
解法
栈模拟
最容易想到的方法是将给定的字符串中的退格符和应当被删除的字符都去除,还原给定字符串的一般形式。然后直接比较两字符串是否相等即可。
具体地,我们用栈处理遍历过程,每次我们遍历到一个字符:
如果它是退格符,那么我们将栈顶弹出;
如果它是普通字符,那么我们将其压入栈中。
class Solution {
public boolean backspaceCompare(String S, String T) {
return build(S).equals(build(T));
}
public String build(String str) {
StringBuilder sb = new StringBuilder();
for (char c : str.toCharArray()) {
if (c == '#') {
// 如果是后退符并且栈顶不为空则pop
if (sb.length() > 0) {
sb.deleteCharAt(sb.length() - 1);
}
// 如果是普通字符则入栈
} else {
sb.append(c);
}
}
return sb.toString();
}
}
复杂度分析
时间复杂度:O(N+M),其中 N 和 M 分别为字符串 S 和 T 的长度。我们需要遍历两字符串各一次。
空间复杂度:O(N+M),其中 N 和 M 分别为字符串 S 和 T 的长度。主要为还原出的字符串的开销。
这里空间复杂度不满足要求,需要进一步优化
双指针
思路及算法
一个字符是否会被删掉,只取决于该字符后面的退格符,而与该字符前面的退格符无关。因此当我们逆序地遍历字符串,就可以立即确定当前字符是否会被删掉。
具体地,我们定义skip 表示当前待删除的字符的数量。每次我们遍历到一个字符:
若该字符为退格符,则我们需要多删除一个普通字符,我们让 skip 加 11;
-
若该字符为普通字符:
-
- 若skip 为 0,则说明当前字符不需要删去;
-
- 若 skip 不为 0,则说明当前字符需要删去,我们让skip 减 1。
这样,我们定义两个指针,分别指向两字符串的末尾。每次我们让两指针逆序地遍历两字符串,直到两字符串能够各自确定一个字符,然后将这两个字符进行比较。重复这一过程直到找到的两个字符不相等,或遍历完字符串为止。
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) {
// S串遍历到没有退格的字符
while (i >= 0) {
if (S.charAt(i) == '#') {
skipS++;
i--;
} else if (skipS > 0) {
skipS--;
i--;
} else {
break;
}
}
// T串遍历到没有退格的字符
while (j >= 0) {
if (T.charAt(j) == '#') {
skipT++;
j--;
} else if (skipT > 0) {
skipT--;
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;
}
// 向前遍历
i--;
j--;
}
return true;
}