@TOC _
序文
コードランダムレコードアルゴリズムトレーニングキャンプ 56日目
1. Leetcode 583. 2 つの文字列の削除操作
1. トピック
2 つの単語 word1 と word2 を指定すると、word1 と word2 を同じにするために必要な最小ステップ数を返します。
各ステップでは、任意の文字列内の 1 文字を削除できます。
例 1:
入力: word1 = "sea"、word2 = "eat" 出力: 2 説明: 最初のステップで "sea" が "ea" に変更され、2 番目のステップで "eat" が "ea" に変更されます。
例 2:
入力: word1 = "leetcode"、word2 = "etc" 出力: 4
ヒント:
1 <= word1.length, word2.length <= 500
word1 和 word2 只包含小写英文字母
ソース: LeetCode リンク: https://leetcode.cn/problems/delete-operation-for-two-strings
2. 問題解決のアイデア
方法 1: 最長共通部分列
2 つの文字列 word1word1 と word2word2 がある場合、いくつかの文字を削除して 2 つの文字列を同じにすると、残りの文字は 2 つの文字列の共通部分列になります。削除操作の数を最小限に抑えるには、残りの文字をできるだけ多くする必要があります。残りの文字が 2 つの文字列の最も長い共通部分列である場合、削除操作の数は最も少なくなります。したがって、2 つの文字列の最長共通部分列の長さを計算し、次に 2 つの文字列の長さと最長共通部分列の長さの差、つまり計算する必要がある文字数を計算できます。 2 つの文字列それぞれについて削除される文字数の合計が、削除操作の最小合計数になります。
最長共通部分列については「1143. 最長共通部分列」を参照してください。最長共通部分列の長さの計算方法については、「1143. 最長共通部分列の公式解法」を参照してください。ここでは詳しく説明しません。
文字列 word1word1 と word2word2 の長さがそれぞれ mm と nn であると仮定すると、文字列 word1word1 と word2word2 の最長の共通部分列の長さを lcslcs で計算すると、削除操作の最小数は m−lcs になります。 +n−lcsm−lcs+n−lcs。
3. コードの実装
```java class Solution { public int minDistance(String word1, String word2) { int m = word1.length(), n = word2.length(); int[][] dp = 新しい int[m + 1][n + 1]; for (int i = 1; i <= m; i++) { char c1 = word1.charAt(i - 1); for (int j = 1; j <= n; j++) { char c2 = word2.charAt(j - 1); if (c1 == c2) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); int lcs = dp[m][n]; m - lcs + n - lcs を返します。} }
「」
2. リートコード 72. 距離の編集
1. トピック
word1 と word2 という 2 つの単語がある場合、word1 を word2 に変換するために使用される演算の最小数を返してください。
単語に対して次の 3 つの操作を実行できます。
插入一个字符
删除一个字符
替换一个字符
例 1:
入力: word1 = "horse"、word2 = "ros" 出力: 3 説明: horse -> rorse ('h' を 'r' に置換) rorse ->rose ('r' を削除)、rose -> ros ('e を削除) ')
例 2:
入力:word1 = "intention"、word2 = "execution" 出力:5 説明:intention -> inention ('t' を削除) inention -> enention ('i' を 'e' に置き換え) enention -> exention ('replace' を置換) n' を 'x' に置き換えます) 拡張 -> 実行 ('n' を 'c' に置き換えます) 実行 -> 実行 ('u' を挿入)
ヒント:
0 <= word1.length, word2.length <= 500
word1 和 word2 由小写英文字母组成
出典: LeetCode リンク: https://leetcode.cn/problems/edit- distance
2. 問題解決のアイデア
方法 1: 動的プログラミング
アイデアとアルゴリズム
任意の単語に対して 3 つの操作を実行できます。
插入一个字符;
删除一个字符;
替换一个字符。
タイトルには 2 つの単語があり、これを A と B として、6 つの操作方法が考えられます。
しかし、単語 A と単語 B がある場合、次のことがわかります。
对单词 A 删除一个字符和对单词 B 插入一个字符是等价的。例如当单词 A 为 doge,单词 B 为 dog 时,我们既可以删除单词 A 的最后一个字符 e,得到相同的 dog,也可以在单词 B 末尾添加一个字符 e,得到相同的 doge;
同理,对单词 B 删除一个字符和对单词 A 插入一个字符也是等价的;
对单词 A 替换一个字符和对单词 B 替换一个字符是等价的。例如当单词 A 为 bat,单词 B 为 cat 时,我们修改单词 A 的第一个字母 b -> c,和修改单词 B 的第一个字母 c -> b 是等价的。
このように、実際には、本質的に異なる操作は 3 つだけです。
在单词 A 中插入一个字符;
在单词 B 中插入一个字符;
修改单词 A 的一个字符。
このようにして、元の問題をより小さなサブ問題に変換できます。A = 馬、B = ロスを例として使用して、この問題がいくつかの小さなサブ問題にどのように変換できるかを見てみましょう。
在单词 A 中插入一个字符:如果我们知道 horse 到 ro 的编辑距离为 a,那么显然 horse 到 ros 的编辑距离不会超过 a + 1。这是因为我们可以在 a 次操作后将 horse 和 ro 变为相同的字符串,只需要额外的 1 次操作,在单词 A 的末尾添加字符 s,就能在 a + 1 次操作后将 horse 和 ro 变为相同的字符串;
在单词 B 中插入一个字符:如果我们知道 hors 到 ros 的编辑距离为 b,那么显然 horse 到 ros 的编辑距离不会超过 b + 1,原因同上;
修改单词 A 的一个字符:如果我们知道 hors 到 ro 的编辑距离为 c,那么显然 horse 到 ros 的编辑距离不会超过 c + 1,原因同上。
次に、馬からロスまでの編集距離は min(a + 1, b + 1, c + 1) になるはずです。
注: なぜ常に単語 A と B の末尾に文字を挿入または変更するのですが、他の場所でも操作できるのでしょうか? 答えは「はい」ですが、操作の順序が最終結果に影響しないことはわかっています。たとえば、単語 cat の場合、c と a の間に文字 d を追加し、文字 t を文字 b に変更すると、これら 2 つの操作の順序に関係なく、最終結果 cdab が得られます。
馬とロの問題も解決が難しいと思われるかもしれません。しかし、それは問題ではありません。上記の方法でこの問題の分割を続けることができ、この問題から分割されたすべてのサブ問題についても、次の状態になるまで分割を続けることができます。
字符串 A 为空,如从 转换到 ro,显然编辑距离为字符串 B 的长度,这里是 2;
字符串 B 为空,如从 horse 转换到 ,显然编辑距离为字符串 A 的长度,这里是 5。
したがって、動的計画法を使用してこの問題を解決できます。A の最初の i 文字と B の最初の j 文字の間の編集距離を D[i][j] で表します。
72_図1.PNG
上で述べたように、D[i][ j] を計算できます。
D[i][j-1] 为 A 的前 i 个字符和 B 的前 j - 1 个字符编辑距离的子问题。即对于 B 的第 j 个字符,我们在 A 的末尾添加了一个相同的字符,那么 D[i][j] 最小可以为 D[i][j-1] + 1;
D[i-1][j] 为 A 的前 i - 1 个字符和 B 的前 j 个字符编辑距离的子问题。即对于 A 的第 i 个字符,我们在 B 的末尾添加了一个相同的字符,那么 D[i][j] 最小可以为 D[i-1][j] + 1;
D[i-1][j-1] 为 A 前 i - 1 个字符和 B 的前 j - 1 个字符编辑距离的子问题。即对于 B 的第 j 个字符,我们修改 A 的第 i 个字符使它们相同,那么 D[i][j] 最小可以为 D[i-1][j-1] + 1。特别地,如果 A 的第 i 个字符和 B 的第 j 个字符原本就相同,那么我们实际上不需要进行修改操作。在这种情况下,D[i][j] 最小可以为 D[i-1][j-1]。
次に、次の状態遷移方程式を書くことができます。
若 A 和 B 的最后一个字母相同:
D[i][j]=min(D[i][j−1]+1,D[i−1][j]+1,D[i−1][j−1])=1+min(D[i][j−1],D[i−1][j],D[i−1][j−1]−1)D[i][j]=min(D[i][j−1]+1,D[i−1][j]+1,D[i−1][j−1])=1+min(D[i][j−1],D[i−1][j],D[i−1][j−1]−1)
若 A 和 B 的最后一个字母不同:
D[i][j]=1+min(D[i][j−1],D[i−1][j],D[i−1][j−1])D[i][j]=1+min(D[i][j−1],D[i−1][j],D[i−1][j−1])
したがって、各ステップの結果は、以下に示すように、前のステップの計算結果に基づきます。
72_図2.PNG
境界の場合、空の文字列と空でない文字列の間の編集距離は D[i][0] = i および D[0][j] = j です。D[i][0] は次の実行と同等です。 word1 に対して i 回の削除操作、D[0][j] を実行することは、word1 に対して j 回の挿入操作を実行することと同等です。
要約すると、アルゴリズムの全プロセスが得られました。
3. コードの実装
```java class Solution { public int minDistance(String word1, String word2) { int n = word1.length(); int m = word2.length();
// 有一个字符串为空串
if (n * m == 0) {
return n + m;
}
// DP 数组
int[][] D = new int[n + 1][m + 1];
// 边界状态初始化
for (int i = 0; i < n + 1; i++) {
D[i][0] = i;
}
for (int j = 0; j < m + 1; j++) {
D[0][j] = j;
}
// 计算所有 DP 值
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < m + 1; j++) {
int left = D[i - 1][j] + 1;
int down = D[i][j - 1] + 1;
int left_down = D[i - 1][j - 1];
if (word1.charAt(i - 1) != word2.charAt(j - 1)) {
left_down += 1;
}
D[i][j] = Math.min(left, Math.min(down, left_down));
}
}
return D[n][m];
}
}
「」