from https://leetcode.com/problems/one-edit-distance/
正如摘要所说,edit distance是通过变换某些位置的字符使得两个字符串相同的操作数;而该问题是一个比较有名的动态规划问题;分别用s和t表示两个字符串,s(i) 表示s中得第i个字符;用f(i, j)表示s(i)和t(j)的edit distance;那么有以下的递推关系:
f(i, j) = min (
f(i - 1, j) + 1; //即删掉s中得第i个字符;
f(i, j - 1) + 1; //即删掉t中得第j个字符;
f(i - 1, j - 1); //如果s(i) == t(j)
f(i - 1, j - 1) + 1; //s(i) != t(j), 所以要做一次替换;
)
所以第一种方案是先计算出所给两个字符串的edit distance,检查是否等于1;当然,这样做会超时,因为通过动态规划计算edit distance的时间复杂度为O(m * n);
但这里还是给出动态规划的代码:
public static int minDistance(String word1, String word2) {
if (word1 == null || word1.length() == 0) {
return word2 == null ? 0 : word2.length();
}
if (word2 == null || word2.length() == 0) {
return word1.length();
}
char[] ss = word1.toCharArray();
char[] ts = word2.toCharArray();
int[][] m = new int[ss.length + 1][ts.length + 1];
for (int i = 0; i < m.length; i++) {
m[i][0] = i;
}
for (int j = 0; j < m[0].length; j++) {
m[0][j] = j;
}
for (int i = 1; i < m.length; i++) {
for (int j = 1; j < m[i].length; j++) {
int a = m[i - 1][j - 1];
if (ss[i - 1] != ts[j - 1]) {
a += 1;
}
int b = m[i - 1][j] + 1;
int c = m[i][j - 1] + 1;
m[i][j] = Math.min(a, Math.min(b, c));
}
}
return m[ss.length][ts.length];
}
其实还是要利用题目中要求的edit distance为1的条件,依次分析以下两个字符串的情况:
如果两个字符串的长度相差多于1,则肯定为false,因为至少需要删除两次字符;
如果两个字符串的长度相同,那么两个字符串中只能有一个位置的字符不同;这种情况只需要扫描一次就可以了;
如果两个字符串的长度相差1,假设s比t长,那么除了s中得某个位置,删掉该字符后,剩余部分应该完全相同;
似乎第三种情况比较难处理,事实上并非如此;依次检查s中得字符,假设处理到了位置i,那么可以知道位置i之前的字符s和t中都是相同的,否则的花,s和t中之前如果已经有一个字符不同的话,edit distance已经至少为1了;如果位置s(i) == t(i), 那么继续处理下一个字符,如果s(i) != t(i), 需要在t的i位置插入一个字符,然后检查剩余的字符是否相同即可;所以,第三种处理也只需要扫描一次;
public boolean isOneEditDistance(String s, String t) {
int m = s.length();
int n = t.length();
if (m == n) {
return checkSameLength(s.toCharArray(), t.toCharArray());
} else if (m - n == 1) {
return checkDiffLength(s.toCharArray(), t.toCharArray());
} else if (n - m == 1) {
return checkDiffLength(t.toCharArray(), s.toCharArray());
} else {
return false;
}
}
private boolean checkDiffLength(char[] s, char[] t) {
for(int i = 0; i < s.length; i++) {
if(i == t.length) {
return true;
}
if(s[i] == t[i]) {
continue;
}
for(int j = i + 1; j < s.length; j++) {
if(s[j] != t[j - 1]) {
return false;
}
}
return true;
}
return false;
}
private boolean checkSameLength(char[] s, char[] t) {
int dist = 0;
for (int i = 0; i < s.length; i++) {
if (s[i] != t[i]) {
dist += 1;
}
if (dist > 1) {
return false;
}
}
return dist == 1;
}