1
+ // 72. 编辑距离
2
+
3
+
4
+ /*
5
+ 动态规划:自底向上
6
+ 1、dp[i][j] 表示 word1 前 i 个字符转换成 word2 前 j 个字符使用的最少操作数
7
+ 注意第 i 个字符在字符串中为 word1.charAt(i - 1),索引从0开始
8
+ 2、初始化:
9
+ 1)针对首行首列要单独考虑,引入''。列表示word1,行表示word2
10
+ 2)第一行是word1为空,word1转换成word2使用的最少操作数,就是插入操作
11
+ 3)第一列是word2为空,word1转换成word2使用的最少操作数,就是删除操作
12
+ 3、状态转移方程:
13
+ 1)当 word1[i] == word2[j],由于遍历到了i、j,说明 word1 的 0~i-1 和 word2 的 0~j-1 的匹配结果已经生成,当前两个字符相同,无需任何操作,因此 dp[i][j] = dp[i - 1][j - 1];
14
+ 2)当 word1[i] != word2[j]
15
+ ① 替换:word1 的 0~i-1 位置与 word2 的 0~j-1 位置的字符都相同,只是当前位置的字符不匹配,进行替换操作后两者变得相同,因此 dp[i][j] = dp[i - 1][j - 1] + 1;
16
+ ② 删除:若此时 word1 的 0~i-1 位置与 word2 的 0~j 位置已经匹配了,此时多出了 word1 的 i 位置字符,应把它删除掉两者才变得相同,因此 dp[i][j] = dp[i - 1][j] + 1;
17
+ ③ 插入:若此时 word1 的 0~i 位置只是和 word2 的 0~j-1 位置匹配,此时只需要在 word1 的 i 位置后面插入一个和 word2 的 j 位置相同的字符,使得两者变得相同,因此 dp[i][j] = dp[i][j - 1] + 1;
18
+ 注:加1表示执行一次操作
19
+ 4、以 word1 为 "horse",word2 为 "ros",且 dp[5][3] 为例,即要将 word1 的前 5 个字符转换为 word2 的前 3 个字符,也就是将 horse 转换为 ros,因此有:
20
+ 1)dp[i-1][j-1] 表示替换操作,即先将 word1 的前 4 个字符 hors 转换为 word2 的前 2 个字符 ro,然后将 word1 第五个字符替换成 word2 的第三个字符,即由 e 替换为 s
21
+ 2)dp[i][j-1] 表示删除操作,即先将 word1 的前 5 个字符 horse 转换为 word2 的前 2 个字符 ro,然后在末尾插入一个 s
22
+ 3)dp[i-1][j] 表示插入操作,即先将 word1 的前 4 个字符 hors 转换为 word2 的前 3 个字符 ros,然后删除 word1 的第 5 个字符
23
+
24
+ '' r o s
25
+ '' 0 1 2 3
26
+ h 1 1 2 3
27
+ o 2 2 1 2
28
+ r 3 2 2 2
29
+ s 4 3 3 2
30
+ e 5 4 4 3
31
+ */
32
+ class Solution {
33
+ public int minDistance (String word1 , String word2 ) {
34
+ int n = word1 .length (), m = word2 .length ();
35
+ int [][] dp = new int [n + 1 ][m + 1 ];
36
+ for (int i = 1 ; i < n + 1 ; i ++) {
37
+ dp [i ][0 ] = dp [i - 1 ][0 ] + 1 ;
38
+ }
39
+ for (int j = 1 ; j < m + 1 ; j ++) {
40
+ dp [0 ][j ] = dp [0 ][j - 1 ] + 1 ;
41
+ }
42
+ for (int i = 1 ; i < n + 1 ; i ++) {
43
+ for (int j = 1 ; j < m + 1 ; j ++) {
44
+ if (word1 .charAt (i - 1 ) == word2 .charAt (j - 1 )) {
45
+ dp [i ][j ] = dp [i - 1 ][j - 1 ];
46
+ } else {
47
+ dp [i ][j ] = 1 + Math .min (dp [i - 1 ][j - 1 ], Math .min (dp [i - 1 ][j ], dp [i ][j - 1 ]));
48
+ }
49
+ }
50
+ }
51
+ return dp [n ][m ];
52
+ }
53
+ }
54
+
55
+
56
+
57
+ /*
58
+ 递归:自顶向下
59
+ 1、定义并初始化备忘录数组,作用相当于dp数组
60
+ 2、使用两个指针i、j分别指向两个字符串的末尾,逐步向前移动
61
+ 3、定义递归函数
62
+ 1)函数定义:dfs(i,j) 输入参数 word1 的位置 i 和 word2 的位置 j,返回 word1[0..i] 和 word2[0..j] 的最小编辑距离
63
+ 2)终止条件:一个字符串遍历完,则返回另一个字符剩余字符个数,表示全部删除或全部插入,都刚好遍历完则是返回0
64
+ 3)查找备忘录,有计算过则快速返回
65
+ 4)调用递归函数计算(i,j)的最小编辑距离,结果保存在备忘录中,并返回结果
66
+
67
+ word1: h r o s e
68
+ ↑
69
+ i
70
+ word2: r o s
71
+ ↑
72
+ j
73
+ */
74
+ class Solution {
75
+ public int minDistance (String word1 , String word2 ) {
76
+ int n = word1 .length (), m = word2 .length ();
77
+ int [][] memo = new int [n ][m ];
78
+ for (int i = 0 ; i < n ; i ++) {
79
+ for (int j = 0 ; j < m ; j ++) {
80
+ memo [i ][j ] = -1 ;
81
+ }
82
+ }
83
+ return dfs (word1 , word2 , n - 1 , m - 1 , memo );
84
+ }
85
+
86
+ private int dfs (String word1 , String word2 , int i , int j , int [][] memo ) {
87
+ if (i == -1 ) {
88
+ return j + 1 ;
89
+ }
90
+ if (j == -1 ) {
91
+ return i + 1 ;
92
+ }
93
+ if (memo [i ][j ] != -1 ) {
94
+ return memo [i ][j ];
95
+ }
96
+ if (word1 .charAt (i ) == word2 .charAt (j )) {
97
+ memo [i ][j ] = dfs (word1 , word2 , i - 1 , j - 1 , memo );
98
+ } else {
99
+ memo [i ][j ] = 1 + Math .min (dfs (word1 , word2 , i - 1 , j - 1 , memo ), Math .min (dfs (word1 , word2 , i , j - 1 , memo ), dfs (word1 , word2 , i - 1 , j , memo )));
100
+ }
101
+ return memo [i ][j ];
102
+ }
103
+ }
0 commit comments