大家好,我是挨打的阿木木,爱好算法的前端摸鱼老。最近会频繁给大家分享我刷算法题过程中的思路和心得。如果你也是想提高逼格的摸鱼老,欢迎关注我,一起学习。
题目
844. 比较含退格的字符串
给定 s
和 t
两个字符串,当它们分别被输入到空白的文本编辑器后,请你判断二者是否相等。#
代表退格字符。
如果相等,返回 true
;否则,返回 false
。
注意: 如果对空文本输入退格字符,文本继续为空。
示例 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”。
提示:
1 <= s.length, t.length <= 200
s
和t
只含有小写字母以及字符'#'
进阶:
- 你可以用
O(N)
的时间复杂度和O(1)
的空间复杂度解决该问题吗?
思路
- 用栈的思想,每次往当前栈
stack
中推入当前字符,遇到#
就从栈中删除掉最后一位元素; - 分别格式化
s
和t
, 比较它们格式化后的结果是否一样; - 由于格式化的方法是一模一样的,所以我们抽离成一个公共方法,减少冗余代码。
实现
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var backspaceCompare = function(s, t) {
return formatString(s) === formatString(t);
};
function formatString(str) {
// 先遍历s字符串,遇到"#"删除一个元素
let stack = [];
const n = str.length;
for (let i = 0; i < n; i++) {
if (str[i] === "#") {
stack.pop();
} else {
stack.push(str[i]);
}
}
// 返回格式化后的字符串
return stack.join("");
}
结果
结果一般般,这显然达不到我们刷题的目的。刷题至少保证时间复杂度到80%以上,最好能在90%。而且题目也说了,进阶要求我们使用空间复杂度O(1), 说明这道题有其他的解法。
优化
这就又回到了找规律的过程了,其实说难也不难,稍微想想,匹配两个字符串,除了用栈我第一个想到的是指针,可是如果从前往后遍历,咱们不知道前面的元素要不要保留。那么不妨换个角度,从后往前遍历,遇到#
了就记录一下当前有几个需要删除的元素,而且从后面开始如果遇到不匹配的就可以直接返回false
了。
优化代码
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
function backspaceCompare(s, t) {
let n = s.length - 1, m = t.length - 1;
// 从倒数第一位一直遍历到第一位
while (n >= 0 && m >= 0) {
// 执行删除,如果有需要的话
n = getFirstChart(s, n);
m = getFirstChart(t, m);
// 开始配对
if (s[n] !== t[m]) {
return false;
}
// 抬走,下一位
n--;
m--;
}
// 看看是否同时为 0, 可能残留了"a#", 所以执行多一次删除看看
return n === m || getFirstChart(s, n) === getFirstChart(t, m);
};
// 找到第一个有效的字符
function getFirstChart(str, len) {
// 记录有多少个待删除元素
let count = 0;
// 遇到删除符号就往前删
// && len > -1 的意思是:如果第一个元素都删掉了,直接返回
while ((str[len] === "#" || count > 0) && len > -1) {
count = str[len] === "#" ? count + 1 : count - 1;
len--;
}
return len;
}
优化结果
看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。