844. 比较含退格的字符串
题目来源:力扣(LeetCode)https://leetcode-cn.com/problems/backspace-string-compare/
题目
给定 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”。
提示:
1 <= S.length <= 200
1 <= T.length <= 200
S
和T
只含有小写字母以及字符'#'
。
进阶:
- 你可以用
O(N)
的时间复杂度和O(1)
的空间复杂度解决该问题吗?
解题思路
思路:栈、双指针
先看题目,题目给定两个字符串 S
和 T
,其中两个字符串中可能含有 #
字符,这里 #
字符,表示退格字符。
题目要求,若将 S
和 T
分别输入文本编辑器,判断是否相等?
其中还有提示及需要注意的部分:
S
和T
只含有小写字母以及字符'#'
。- 空文本输入 “#” 字符,文本最终还是为空。
S
和T
两者的长度都大于或等于 1,小于或等于 200。
这里看看题目示例 1 和示例 2:
示例 1
输入:S = "ab#c", T = "ad#c"
-
S
字符串中,字符 b 后面紧跟#
字符,那么 b 字符将会被删除,所以这里最终输出的结果会是 ac; -
T
字符串中,字符 d 后面跟#
字符,那么 d 字符将会被删除,所以输出的结果也是 ac。
所以两者相同。
示例 2
输入:S = "ab##", T = "c#d#"
这里跟示例 1 不同的地方在于,这里存在两个 “#” 字符。这里也看看是如何变化的?
-
S
字符串中,字符 b 后面紧跟两个#
字符。首先先明确在文本输入字符串时,是按照顺序输入的,那么字符 b 遇到第一个#
时,先将字符 b 删除,剩下字符 a,再次遇到第二个#
时,a 字符被删除,那么最终文本为空; -
T
字符串中,这里比较好理解,就是输入 c 字符,然后被删除,接着输入字符 d,再次删除,最终文本也为空。
所以这两者都相同。
栈
结合这两个示例看,不管 #
字符有多少,只要遇到 #
字符,(除空文本外)总有对应的字符需要被删除。那么我们可以使用栈来模拟这个过程,遍历字符串,判断其中的字符:
- 如果字符为非
#
符号,那么将字符压入栈中; - 如果字符为
#
符号,那么将栈顶的字符弹出。
这里需要注意,本篇文章用 list 实现栈,这里 list 为空时需要注意弹出元素会抛出异常。
具体代码实现如下:
class Solution:
def backspaceCompare(self, S: str, T: str) -> bool:
def text_input(text):
# 栈
stack = []
# 遍历字符串
for ch in text:
# 普通字符入栈
if ch != "#":
stack.append(ch)
# "#" 字符且栈非空时,弹出
elif stack:
stack.pop()
return ''.join(stack)
# 判断两者是否相同
return text_input(S) == text_input(T)
复杂度分析
- 时间复杂度:
O
(
n
+
m
)
O(n+m)
O(n+m),
n
n
n 和
m
m
m 分别表示
S
和T
两个字符串的长度。这里需要分别遍历两个字符串一次。 - 空间复杂度: O ( n + m ) O(n+m) O(n+m)。
双指针
根据前面对示例的分析,我们可以发现,#
字符只会影响其前面的普通字符,也就是说对于普通字符是否会被删除,取决于它后面的 #
字符。那么,我们可以考虑逆序遍历字符串,判断是否存在 #
字符,进而确定是否需要删除字符。
再看示例 2,
输入:S = "ab##", T = "c#d#"
这里 S
有两个连续 #
,这里逆序遍历时,不能单纯的仅对前一位字符进行判断。在这里说下具体的做法:
- 首先定义双指针,分别指向两个字符串的末尾,开始逆序遍历;
- 定义变量 cnt,这里用来记录
#
出现的次数; - 当遇到
#
时,那么 cnt 要相应加 1; - 如果遇到的是普通字符,这里根据 cnt 来判断是否需要删除字符:
- 当 cnt 为 0 时,表示当前普通字符后面没有
#
字符,此处不需要变动; - 若 cnt 不为 0,那么这里要该字符要删除,同时更新 cnt,让 cnt 减 1。
- 当 cnt 为 0 时,表示当前普通字符后面没有
- 遍历完字符串,或遍历找到的字符不同时终止,返回对应的结果。
在将前面的做法实现时,遍历字符串,根据 #
的数量 cnt 对字符串进行处理,会遇到这样的情况:
- cnt 为 0,两指针都指向某个字符时,这里要先判断字符,若相同,继续判断;若不同,则直接返回 False;
- 若其中仅有一个字符串已经遍历完成时,那么这里应该直接返回 False。因为另外一个字符串未遍历完成,一定是停在某个字符(假设为 a)等待判断,而字符串遍历完成,也就表示并未出现需要判断的字符,也就没有字符能与 a 比较判断,这里也就能直接给出两字符串不同的结论。
- 两个字符串都遍历完成,返回 True。
具体的代码实现如下。
class Solution:
def backspaceCompare(self, S: str, T: str) -> bool:
# 定义指针,p 指向 S 末尾,q 指向 T 末尾
p = len(S) - 1
q = len(T) - 1
# 定义变量 cnt_S,cnt_T,分别 '#' 的数量变化
cnt_S = 0
cnt_T = 0
while p >= 0 or q >= 0:
# 先遍历字符串 S
while p >= 0:
# 字符为 '#',cnt_S 加 1,同时 p 指针往左移动
if S[p] == '#':
cnt_S += 1
p -= 1
# 字符不为 '#',且 cnt_S 不为 0,当前字符要删除,更新 cnt_S,移动 p 指针
elif cnt_S > 0:
cnt_S -= 1
p -= 1
# 为普通字符,且 cnt_S 为 0,跳出循环,等待判断
else:
break
# 遍历字符串 T
# 逻辑与上面相同
while q >= 0:
if T[q] == '#':
cnt_T += 1
q -= 1
elif cnt_T > 0:
cnt_T -= 1
q -= 1
else:
break
# 此时判断两指针对应的字符
# 字符串都未遍历完的情况
if p >= 0 and q >= 0:
# 对应字符不同,直接返回 False
if S[p] != T[q]:
return False
# 字符串只有一个遍历完的情况
elif p >= 0 or q >= 0:
return False
# 上面情况不满足时,需要继续判断,继续移动 p, q 指针
p -= 1
q -= 1
return True
复杂度分析
- 时间复杂度:
O
(
n
+
m
)
O(n+m)
O(n+m),
n
n
n 和
m
m
m 分别表示
S
和T
两个字符串的长度。这里需要分别遍历两个字符串一次。 - 空间复杂度: O ( 1 ) O(1) O(1)。仅使用了常数个变量。
欢迎关注
公众号 【书所集录】