参照ブログ:https://github.com/youngwind/blog/issues/106
2つの単語word1とword2が与えられた場合、word1をword2に変換するために使用されるオペランドの最小数を計算します。
ワードに対して次の3つの操作を実行できます。
文字
を挿入する文字を削除する文字を
置き換える
例1:
入力:word1 = "horse"、word2 = "ros"
出力:3
説明:
horse-> rorse( '
h 'を 'r'に置き換えます)rorse-> rose(delete'r ')rose-
> ros(delete'e ')
出典:LeetCode
リンク:https ://leetcode-cn.com/problems/edit-distance
著作権はLeetCodeが所有しています。商用の再版については、公式の承認に連絡してください。非商用の再版については、出典を示してください。
与えられた2つの単語に対して、最も少ないステップでword1をword2に変換する方法は?インターネットでいくつかの解決策を学びました。再帰的および動的プログラミングの場合、2つの方法は異なる方法で実装されますが、基本的な考え方は同じです。アイデアは次のとおりです
。2つの単語について、ある単語を別の単語に変換する場合は、単語の各文字を考慮して、他の単語の各文字と同じにする必要があります。つまり、対応する編集を行います。 2つの文字が同じ場合は、順番に編集する必要がある他の文字を編集します
- d [i] [j]が、word1の長さiの部分文字列からword2の長さjの部分文字列までの最小および最小編集距離を表すとします。
- iとjがそれぞれ対応する文字列の長さに等しい場合、つまり、d [word1.lenggth()] [word2.length()]が必要です。
- 長さiと長さjの部分文字列の場合、2つの部分文字列の最後の文字が等しい場合、つまり、word1.charAt(i-1)== word2.charAt(j-1)の
場合、2つの部分文字列の編集距離d [i] [j] = d [i-1] [j-1]、これは長さi-1および長さj-1の部分文字列の編集距離に等しい。これは同じであるため理解しやすい。編集する必要はありません。 - 2つの部分文字列の最後の文字が等しくない場合、つまり、word1.charAt(i-1)!= word2.charAt(j-1)、現時点ではそれに応じて編集する必要があります、
- 編集方法1:d [i] [j] = d [i] [j-1] + 1
- :文字列abcとacdの場合、2つの文字列の最後の文字が等しくありません。
- :文字を追加できます。dを追加すると、abcがabcdになります。
- :abcdからacdまでの脚本距離は、abcからacまでの編集距離と同じです。これは、dが等しいため、つまり、d [i] [j] = d [i] [j-1] + 1
- :実際、ここに文字を追加すると、acdの文字dを削除するのと同じ効果があります。
- 編集方法2:d [i] [j] = d [i-1] [j] + 1
- :文字cを追加すると、acdはacdcになります
- :abcからacdへの編集距離は、cが等しいため、abからacdへの編集距離になります。つまり、d [i] [j] = d [i-1] [j] + 1
- :ここで文字cを追加する方法は、abcで文字cを削除する方法と同じです。
- 編集方法3:d [i] [j] = d [i-1] [j-1] + 1
- :文字を置き換えることで、abcの最後の文字をdに置き換えることができます。つまり、abdをacdに置き換えることができます(abcをaccに置き換えることもできます)
- :対応する編集距離は次のようになります。d[i] [j [] = d [i -1] [j-1] +1
上記の3つの変換方法では、3つの最小値を使用して、対応する最小編集距離を取得します。
ソリューションのコーディングの過程で
- 動的計画法の場合、対応する初期値を与える必要があります。
- 再帰的方法の場合、境界値を与える必要があります
class 编辑距离 {
//递归解法
public static int minDistanceWay1(String word1, String word2) {
// 减少重复运算,不然会超时
int[][] dis = new int[word1.length() + 1][word2.length() + 1];
for (int i = 0; i <= word1.length(); i++) {
Arrays.fill(dis[i], -1);
}
return recursion(word1, word2, word1.length(), word2.length(), dis);
}
private static int recursion(String word1, String word2, int length1, int length2, int[][] dis) {
if (dis[length1][length2] > -1) {
return dis[length1][length2];
}
if (length1 == 0) {
return length2;
} else if (length2 == 0) {
return length1;
} else if (word1.charAt(length1 - 1) == word2.charAt(length2 - 1)) {
// 两个字符相等的话我们就直接比较前一个字符
int distance = recursion(word1, word2, length1 - 1, length2 - 1, dis);
dis[length1][length2] = distance;
return distance;
} else {
// 删除字符和添加字符我们达到的效果是一样的
// 当两个字符串不相等的时候我们就删除word1的最后一个字符,计算(m-1,n)这两个字符串之间的最短编辑距离
int deleteWord1 = recursion(word1, word2, length1 - 1, length2, dis);
// 删除word2的最后一个字符
int deleteWord2 = recursion(word1, word2, length1, length2 - 1, dis) + 1;
// 替换一个word1或者word2中的任意一个字符
int replaceWord = recursion(word1, word2, length1 - 1, length2 - 1, dis);
int distance = Math.min(Math.min(deleteWord1, deleteWord2), replaceWord) + 1;
dis[length1][length2] = distance + 1;
return distance;
}
}
// 动态规划解法
public static int minDistanceWay2(String word1, String word2) {
int lenWord1 = word1.length();
int lenword2 = word2.length();
// minDis[i][j]表示长度为i和长度为j的单词之间的最小编辑距离
int[][] minDis = new int[lenWord1 + 1][lenword2 + 1];
for (int i = 1; i <= lenWord1; i++) {
minDis[i][0] = i;
}
for (int j = 1; j <= lenword2; j++) {
minDis[0][j] = j;
}
for (int i = 1; i <= lenWord1; i++) {
for (int j = 1; j <= lenword2; j++) {
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
minDis[i][j] = minDis[i - 1][j - 1];
} else {
int deleteWord1 = minDis[i - 1][j] + 1;
int deleteWord2 = minDis[i][j - 1] + 1;
int replaceWord = minDis[i - 1][j - 1] + 1;
minDis[i][j] = Math.min(Math.min(deleteWord1, deleteWord2), replaceWord);
}
}
}
return minDis[word1.length()][word2.length()];
}
public static void main(String[] args) {
System.out.println(minDistanceWay2("b", ""));
System.out.println(minDistanceWay2("dinitrophenylhydrazine", "benzalphenylhydrazone"));
}
}```