力扣面试题 01.05. 一次编辑
字符串有三种编辑操作:插入一个字符、删除一个字符或者替换一个字符。 给定两个字符串,编写一个函数判定它们是否只需要一次(或者零次)编辑。
示例 1:
输入:
first = “pale”
second = “ple”
输出: True
示例 2:
输入:
first = “pales”
second = “pal”
输出: False
思路分析:首先肯定是分析是否只需要1次或者0次,0次就是输入二者相等的情况。然后如果两个字符串的长度之差超过了1,那么无论怎么编辑都是不能得到的。所以只看长度差为1和长度相等的情况。
长度相等:一定是替换一个字符,这就可以通过遍历字符串,逐个比较每个字符,记录不相等字符的个数,遍历完后如果个数大于1,那么无法通过替换操作来实现两个字符串相等。反之则可以。
长度相差1:需要判断是否可以通过插入或者删除一个字符来实现二者相等。
官方题解的思路,都从头开始遍历字符串,逐个比较每个字符,这里定义两个变量(index1指向第一个字符串的第一个字符,index2指向第二个字符串的第一个字符)如果相等,那么同时走向下一个字符串(index1++,index2++),如果不等,那么长的字符串走向下一个字符继续比较(仅index2++),短的字符串还是停在当前字符,如果还是不等,长的字符串再走向下一个字符继续比较(index2++),并且每次比较完字符后需要判断当前两字符串遍历的字符下标之差(index2-index1),如果大于1,则表示不能通过插入或删除完成两字符串相等。
其实思路就是,如果两个字符串可以通过插入或者删除一个字符来实现二者相等。那么只有在插入处有不同。例如"abcdefg"与"abdefg",在ab是相等的,要想插入一个就实现二者相等,那么只有长的字符串跳一个字符即跳过c,后面的都相等才可以,如果跳过了c还是不等,那么可以直接return false了。
官方题解代码:
class Solution {
public boolean oneEditAway(String first, String second) {
int m = first.length(), n = second.length();
if (n - m == 1) {
return oneInsert(first, second);
} else if (m - n == 1) {
return oneInsert(second, first);
} else if (m == n) {
boolean foundDifference = false;
for (int i = 0; i < m; i++) {
if (first.charAt(i) != second.charAt(i)) {
if (!foundDifference) {
foundDifference = true;
} else {
return false;
}
}
}
return true;
} else {
return false;
}
}
public boolean oneInsert(String shorter, String longer) {
int length1 = shorter.length(), length2 = longer.length();
int index1 = 0, index2 = 0;
while (index1 < length1 && index2 < length2) {
if (shorter.charAt(index1) == longer.charAt(index2)) {
index1++;
}
index2++;
if (index2 - index1 > 1) {
return false;
}
}
return true;
}
}
其实这种在实现上还有点麻烦。我觉得这种思路写可能要好一点:依然是逐个比较字符串1和字符串2,如果比较到不相等的位置了。那么记录这个位置,例如第4个位置两个字符串的字符不相等,即前三个位置的字符都是相等的,那么这个时候短的字符串从第四个字符开始遍历,长的字符串从第4+1个字符开始遍历,继续逐个比较,如果可以通过插入或者删除一个字符来实现二者相等的话,那么后续的遍历比较过程中是不会出现两字符不相等的,如果出现了就return false。
代码如下(修改了比较的方法)
class Solution {
public boolean oneEditAway(String first, String second) {
if(first.equals(second)) return true;
int n = Math.abs(second.length()-first.length());//长度之差
if(n>1) return false;//超过2位
//先判断能否插入或删除
if(n==1){
if(first.length()>second.length()){
int index = -1;//记录不同字符的坐标
for (int i = 0; i < second.length(); i++) {
if(first.charAt(i)!=second.charAt(i)){//出现字符不等的位置
index = i;
break;
}
}
//如果前面字符都相等
if(index == -1) return true;
//在出现不相等位置后面继续逐个遍历
for (int i = index; i < second.length(); i++) {
//后面出现不相等
if(first.charAt(i+1)!=second.charAt(i)) return false;
}
//后面都相等
return true;
}else {
//和前面一样的,其实可以封装成一个函数,代码看起来简洁些
int index = -1;
for (int i = 0; i < first.length(); i++) {
if(first.charAt(i)!=second.charAt(i)){
index = i;
break;
}
}
if(index == -1) return true;
for (int i = index; i < first.length(); i++) {
if(second.charAt(i+1)!=first.charAt(i)) return false;
}
return true;
}
}else {//替换一个字符的情况
int cnt = 0;
for (int i = 0; i < first.length(); i++) {
if(first.charAt(i)!=second.charAt(i)) cnt++;
}
if(cnt<=1) return true;
}
return false;
}
}
其实我自己做的是另一种思路,其实插入一个字符和另一个字符相等可以等价于长的字符删掉一个字符和短的相等,那么插入和删除都是一样的了,只需要找到长字符,然后删第一个字符,将剩余的字符串和短的相比较,看是否相等,不等的话就删第二个字符,这样逐个删除字符,直至子字符串和短的字符串相等即可。那么问题就变成了如何实现删除字符串中的指定字符这个函数,可以使用substring这个函数,比如要删除第3个字符,那么取0-2以及4到length()这两个子字符串拼接即可。
代码如下:
class Solution {
public boolean oneEditAway(String first, String second) {
if(first.equals(second)) return true;
int n = Math.abs(second.length()-first.length());//长度之差
if(n>1) return false;//超过2位
//先判断能否插入或删除
if(n==1){
int len = 0;
if(first.length()>second.length()){
len = first.length();
for (int i = 0; i < len; i++) {
//删除第i个字符后判断是否和短的字符串相等
if(deleteChar(first,i).equals(second)) return true;
}
}else {
len = second.length();
for (int i = 0; i < len; i++) {
//删除第i个字符后判断是否和短的字符串相等
if(deleteChar(second,i).equals(first)) return true;
}
}
}else {//替换情况
int cnt = 0;
for (int i = 0; i < first.length(); i++) {
if(first.charAt(i)!=second.charAt(i)) cnt++;
}
if(cnt<=1) return true;
}
//不可以的情况包括不能插入删除和替换都会执行到这里
return false;
}
//实现删除字符串s中指定位置的字符,并返回子字符串
private String deleteChar(String s, int index) {
//删除的位置是第一个字符
if(index == 0) return s.substring(1,s.length());
//删除的位置是最后一个字符
if(index == s.length()-1) return s.substring(0,s.length()-1);
//中间的情况
String s1 = s.substring(0,index);
String s2 = s.substring(index+1,s.length());
return s1+s2;
}
}