比较含退格的字符串
问题描述
给定 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)
的空间复杂度解决该问题吗?
解题思路
方法一:直接比较法
最简单的方法就是直接将两字符串中的#
和其前一个字符去掉,比较剩余的字符是否相等。若相等则返回true
。
我们如何在最好的时间内去掉#
和其前一个字符呢?
在不考虑空间开销的情况下,我们可以考虑构造两个辅助栈来完成操作。如果遇到#
,那么就取出栈顶的元素,这样也就模拟了删掉#
前一个字符的效果。那么最后栈中剩余的元素就是去掉退格字符后的所有剩余字符。然后就可以将两个栈中的字符依次比较是否相同。这种方法的代码如下:
class Solution {
public boolean backspaceCompare(String S, String T) {
Stack<Character> stack1 = new Stack<>();
Stack<Character> stack2 = new Stack<>();
for(int i=0; i<S.length(); i++){
if(S.charAt(i)!='#') stack1.add(S.charAt(i));
else if(!stack1.isEmpty()) stack1.pop();
}
for(int j=0; j<T.length(); j++){
if(T.charAt(j)!='#') stack2.add(T.charAt(j));
else if(!stack2.isEmpty()) stack2.pop();
}
return stack1.equals(stack2);
}
}
由于额外创建了两个辅助栈,所以时间复杂度还是有一些偏高。我们再尝试做一些改进:
那么本题,确实可以使用栈的思路,但是没有必要使用栈,因为最后比较的时候还要比较栈里的元素,有点麻烦。为了减轻开销,可以使用拼接字符串的方式将剩余的字符进行拼接,最后进行比较的是便是两个字符串。这样就会比比较两个集合的元素是否相同还要更快。
我们使用Java
中的StringBuffe
r类来实现字符串的拼接。如果没有遇到#字符,则将该字符拼接到StringBuffer
的对象中,否则删除StringBuffer
对象的最后一个字符。最后返回两者对象的字符串进行比较。代码实现如下:
class Main {
public static void backspaceCompare(String S, String T) {
build(S).equals(build(T));
}
public static String build(String str) {
StringBuffer sb = new StringBuffer();
int len = str.length();
for (int i = 0; i < len; i++) {
if (str.charAt(i) != '#')
sb.append(str.charAt(i));
else if (sb.length() > 0)
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
}
}
两次的结果进行对比可以看到,从时间复杂度上,改进之后的方法执行时间更短。但是两次方法的内存消耗没有什么大的变化。有没有一种方法,既可以保持这种优秀的执行时间还保证内存消耗的更少呢?下面就介绍这种双指针的方法来减少内存消耗。
方法二:双指针法:
同时从后向前遍历S
和T(i
初始为S
末尾,j
初始为T
末尾),记录#
的数量,模拟消除的操作,如果#
用完了,就开始比较S[i]
和S[j]
。
具体地,我们定义 skip
表示当前待删除的字符的数量。每次我们遍历到一个字符:
若该字符为退格符,则我们需要多删除一个普通字符,我们让
skip
加1
;若该字符为普通字符:
若
skip
为0
,则说明当前字符不需要删去;若
skip
不为0
,则说明当前字符需要删去,我们让skip
减1
。

如果S[i]
和S[j]
不相同返回false
,如果有一个指针(i
或者j
)先走到的字符串头部位置,也返回false
。
代码实现
class Solution {
public boolean backspaceCompare(String S, String T) {
int skipS = 0, skipT = 0;
int i = S.length()-1, j = T.length()-1;
while(true){
while(i>=0){
if(S.charAt(i) == '#') skipS++;
else{
if(skipS>0) skipS--;
else break;
}
i--;
}
while(j>=0){
if(T.charAt(j) == '#') skipT++;
else{
if(skipT>0) skipT--;
else break;
}
j--;
}
if(i<0 || j<0) break;
if(S.charAt(i)!=T.charAt(j)) return false;
i--;j--;
}
if(i == -1 && j == -1) return true;
return false;
}
}
复杂度分析
时间复杂度:O(N+M),其中 N 和 M 分别为字符串 S 和 T 的长度。我们需要遍历两字符串各一次。
空间复杂度:O(1)。对于每个字符串,我们只需要定义一个指针和一个计数器即可。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!