一、题目描述
给你两个单词 word1 和 word2,请你计算出将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
-
1、插入一个字符
-
2、删除一个字符
-
3、替换一个字符
二、题目解析
三、参考代码
1、Java 代码
class Solution { public int minDistance(String word1, String word2) { // 获取字符串 word1 的长度 int L1 = word1.length(); // 获取字符串 word2 的长度 int L2 = word2.length(); // 设置二维数组 dp 用来储存 word1 的前 i 个字符转换为 word2 的前 j 个字符最少操作数 // dp[0][0] 表示 word1 的前 0 个字符转换为 word2 的前 0 个字符最少操作数 // dp[0][j] 表示 word1 的前 0 个字符转换为 word2 的前 j 个字符最少操作数 // dp[i][0] 表示 word1 的前 i 个字符转换为 word2 的前 0 个字符最少操作数 // dp[i][j] 表示 word1 的前 i 个字符转换为 word2 的前 j 个字符最少操作数 // dp[L1][L2] 表示 word1 的前 L1 个字符转换为 word2 的前 L2 个字符最少操作数 int[][] dp = new int[L1 + 1][L2 + 1]; // dp[i][0] 表示 word1 的前 i 个字符转换为 word2 的前 0 个字符最少操作数 // dp[i][0] 相当于给二维数组 dp 的第一列进行赋值操作 // 只需要每次执行删除操作,就可以把 word1 的字符删除到 0 个字符 for(int i = 0 ; i <= L1 ; i++){ // dp[1][0] 表示需要删除 1 次才能删除到 0 个字符 // dp[2][0] 表示需要删除 2 次才能删除到 2 个字符 // dp[i][0] 表示需要删除 i 次才能删除到 2 个字符 dp[i][0] = i; } // dp[0][j] 表示 word1 的前 0 个字符转换为 word2 的前 j 个字符最少操作数 // dp[0][j] 相当于给二维数组 dp 的第一行进行赋值操作 // 只需要每次执行插入操作,就可以把 word1 的字符转行为 j 个字符 for(int j = 0 ;j <= L2 ; j++){ // dp[0][1] 表示需要插入 1 次才能得到 1 个字符 // dp[0][2] 表示需要插入 2 次才能得到 2 个字符 // dp[0][j] 表示需要插入 j 次才能得到 j 个字符 dp[0][j] = j; } // 通过两个 for 循环,设置二维数组 dp 中的所有元素的值 // i 从 word1 的第 1 个字符一直到 L1 个字符 for(int i = 1 ; i <= L1 ; i++){ // j 从 word2 的第 1 个字符一直到 L2 个字符 for(int j = 1 ; j <= L2 ; j++){ // 二维数组 dp 的下标是从 (0,0) 开始的 // 所以 dp[i][j] 表示的是 word1 中前 i 个字符即位置为 i - 1 的字符 // 转换为 word2 中前 j 个字符即位置为 j - 1 的字符所需要的最少操作数 // 如果此时 word1 中第 i - 1 个位置的字符(即当前遍历 i 时的字符) // 与 word2 中第 j - 1 个位置的字符【相等】(即当前遍历 j 时的字符) // 说明 word1 的前 i - 1 个字符转换为 word2 中前 j - 1 个字符成功后, // word1 的前 i 个字符也就成功转换为 word2 的前 j 个字符了 if(word1.charAt(i - 1) == word2.charAt(j - 1)){ // dp[i][j] 表示 word1 的前 i 个字符转换为 word2 的前 j 个字符最少操作数 // dp[i - 1][j - 1] 表示 word1 的前 i - 1 个字符转换为 word2 的前 j -1 个字符最少操作数 // 比如 word1 为 abcd,word2 为 efgd // 此时 i = 4,j = 4 // 由于 word1.charAt(4 - 1) = d // word2.charAt(4 - 1) = d // 所以如果知道了 abc 转换为 efg 的最少操作数,那么结果也就出来了 // d --> d 不需要执行任何操作 dp[i][j] = dp[i - 1][j - 1]; // 否则,说明 word1 中第 i - 1 个位置的字符(即当前遍历 i 时的字符) // 与 word2 中第 j - 1 个位置的字符【不相等】(即当前遍历 j 时的字符) }else{ // 那么 dp[i][j] 可以从以下三种状态转换过来 // 1、word1(L1-1) ——> word2(L2-1) // 2、word1(L1-1) ——> word2(L2) // 3、word1(L1) ——> word2(L2-1) // word1(L1-1) ——> word2(L2-1) 的最少操作数是 dp[i - 1][j - 1] // word1(L1-1) ——> word2(L2) 的最少操作数是 dp[i - 1][j] // word1(L1) ——> word2(L2-1) 的最少操作数是 dp[i][j - 1]) // 选这三种状态的较小值后, // 1、如果是 word1(L1-1) ——> word2(L2-1) 过来的,再执行 1 次替换操作 // 2、如果是 word1(L1-1) ——> word2(L2) 过来的,再执行 1 次删除操作 // 3、如果是 word1(L1) ——> word2(L2-1) 过来的,再执行 1 次插入操作 dp[i][j] = Math.min(dp[i - 1][j - 1],Math.min(dp[i - 1][j],dp[i][j - 1])) + 1; } } } // dp[L1][L2] 表示 word1 的前 L1 个字符转换为 word2 的前 L2 个字符最少操作数 // 把这个结果进行返回 return dp[L1][L2]; } }
Java
Copy
2、C++ 代码
class Solution { public: int minDistance(string word1, string word2) { // 获取字符串 word1 的长度 int L1 = word1.length(); // 获取字符串 word2 的长度 int L2 = word2.length(); // 设置二维数组 dp 用来储存 word1 的前 i 个字符转换为 word2 的前 j 个字符最少操作数 // dp[0][0] 表示 word1 的前 0 个字符转换为 word2 的前 0 个字符最少操作数 // dp[0][j] 表示 word1 的前 0 个字符转换为 word2 的前 j 个字符最少操作数 // dp[i][0] 表示 word1 的前 i 个字符转换为 word2 的前 0 个字符最少操作数 // dp[i][j] 表示 word1 的前 i 个字符转换为 word2 的前 j 个字符最少操作数 // dp[L1][L2] 表示 word1 的前 L1 个字符转换为 word2 的前 L2 个字符最少操作数 auto dp = vector< vector <int> > ( L1 + 1,vector <int> ( L2 + 1 )); // dp[i][0] 表示 word1 的前 i 个字符转换为 word2 的前 0 个字符最少操作数 // dp[i][0] 相当于给二维数组 dp 的第一列进行赋值操作 // 只需要每次执行删除操作,就可以把 word1 的字符删除到 0 个字符 for(int i = 0 ; i <= L1 ; i++){ // dp[1][0] 表示需要删除 1 次才能删除到 0 个字符 // dp[2][0] 表示需要删除 2 次才能删除到 2 个字符 // dp[i][0] 表示需要删除 i 次才能删除到 2 个字符 dp[i][0] = i; } // dp[0][j] 表示 word1 的前 0 个字符转换为 word2 的前 j 个字符最少操作数 // dp[0][j] 相当于给二维数组 dp 的第一行进行赋值操作 // 只需要每次执行插入操作,就可以把 word1 的字符转行为 j 个字符 for(int j = 0 ;j <= L2 ; j++){ // dp[0][1] 表示需要插入 1 次才能得到 1 个字符 // dp[0][2] 表示需要插入 2 次才能得到 2 个字符 // dp[0][j] 表示需要插入 j 次才能得到 j 个字符 dp[0][j] = j; } // 通过两个 for 循环,设置二维数组 dp 中的所有元素的值 // i 从 word1 的第 1 个字符一直到 L1 个字符 for(int i = 1 ; i <= L1 ; i++){ // j 从 word2 的第 1 个字符一直到 L2 个字符 for(int j = 1 ; j <= L2 ; j++){ // 二维数组 dp 的下标是从 (0,0) 开始的 // 所以 dp[i][j] 表示的是 word1 中前 i 个字符即位置为 i - 1 的字符 // 转换为 word2 中前 j 个字符即位置为 j - 1 的字符所需要的最少操作数 // 如果此时 word1 中第 i - 1 个位置的字符(即当前遍历 i 时的字符) // 与 word2 中第 j - 1 个位置的字符【相等】(即当前遍历 j 时的字符) // 说明 word1 的前 i - 1 个字符转换为 word2 中前 j - 1 个字符成功后, // word1 的前 i 个字符也就成功转换为 word2 的前 j 个字符了 if(word1[i - 1] == word2[j - 1]){ // dp[i][j] 表示 word1 的前 i 个字符转换为 word2 的前 j 个字符最少操作数 // dp[i - 1][j - 1] 表示 word1 的前 i - 1 个字符转换为 word2 的前 j -1 个字符最少操作数 // 比如 word1 为 abcd,word2 为 efgd // 此时 i = 4,j = 4 // 由于 word1.charAt(4 - 1) = d // word2.charAt(4 - 1) = d // 所以如果知道了 abc 转换为 efg 的最少操作数,那么结果也就出来了 // d --> d 不需要执行任何操作 dp[i][j] = dp[i - 1][j - 1]; // 否则,说明 word1 中第 i - 1 个位置的字符(即当前遍历 i 时的字符) // 与 word2 中第 j - 1 个位置的字符【不相等】(即当前遍历 j 时的字符) }else{ // 那么 dp[i][j] 可以从以下三种状态转换过来 // 1、word1(L1-1) ——> word2(L2-1) // 2、word1(L1-1) ——> word2(L2) // 3、word1(L1) ——> word2(L2-1) // word1(L1-1) ——> word2(L2-1) 的最少操作数是 dp[i - 1][j - 1] // word1(L1-1) ——> word2(L2) 的最少操作数是 dp[i - 1][j] // word1(L1) ——> word2(L2-1) 的最少操作数是 dp[i][j - 1]) // 选这三种状态的较小值后, // 1、如果是 word1(L1-1) ——> word2(L2-1) 过来的,再执行 1 次替换操作 // 2、如果是 word1(L1-1) ——> word2(L2) 过来的,再执行 1 次删除操作 // 3、如果是 word1(L1) ——> word2(L2-1) 过来的,再执行 1 次插入操作 dp[i][j] = min(dp[i - 1][j - 1],min(dp[i - 1][j],dp[i][j - 1])) + 1; } } } // dp[L1][L2] 表示 word1 的前 L1 个字符转换为 word2 的前 L2 个字符最少操作数 // 把这个结果进行返回 return dp[L1][L2]; } };
C++
Copy
3、Python 代码
class Solution: def minDistance(self, word1: str, word2: str) -> int: # 获取字符串 word1 的长度 L1 = len(word1) # 获取字符串 word2 的长度 L2 = len(word2) # 设置二维数组 dp 用来储存 word1 的前 i 个字符转换为 word2 的前 j 个字符最少操作数 # dp[0][0] 表示 word1 的前 0 个字符转换为 word2 的前 0 个字符最少操作数 # dp[0][j] 表示 word1 的前 0 个字符转换为 word2 的前 j 个字符最少操作数 # dp[i][0] 表示 word1 的前 i 个字符转换为 word2 的前 0 个字符最少操作数 # dp[i][j] 表示 word1 的前 i 个字符转换为 word2 的前 j 个字符最少操作数 # dp[L1][L2] 表示 word1 的前 L1 个字符转换为 word2 的前 L2 个字符最少操作数 dp = [[0] * (L2 + 1) for _ in range( L1 + 1)] # dp[i][0] 表示 word1 的前 i 个字符转换为 word2 的前 0 个字符最少操作数 # dp[i][0] 相当于给二维数组 dp 的第一列进行赋值操作 # 只需要每次执行删除操作,就可以把 word1 的字符删除到 0 个字符 for i in range(L1 + 1): # dp[1][0] 表示需要删除 1 次才能删除到 0 个字符 # dp[2][0] 表示需要删除 2 次才能删除到 2 个字符 # dp[i][0] 表示需要删除 i 次才能删除到 2 个字符 dp[i][0] = i # dp[0][j] 表示 word1 的前 0 个字符转换为 word2 的前 j 个字符最少操作数 # dp[0][j] 相当于给二维数组 dp 的第一行进行赋值操作 # 只需要每次执行插入操作,就可以把 word1 的字符转行为 j 个字符 for j in range( L2 + 1): # dp[0][1] 表示需要插入 1 次才能得到 1 个字符 # dp[0][2] 表示需要插入 2 次才能得到 2 个字符 # dp[0][j] 表示需要插入 j 次才能得到 j 个字符 dp[0][j] = j # 通过两个 for 循环,设置二维数组 dp 中的所有元素的值 # i 从 word1 的第 1 个字符一直到 L1 个字符 for i in range( 1 , L1 + 1): # j 从 word2 的第 1 个字符一直到 L2 个字符 for j in range( 1 , L2 + 1): # 二维数组 dp 的下标是从 (0,0) 开始的 # 所以 dp[i][j] 表示的是 word1 中前 i 个字符即位置为 i - 1 的字符 # 转换为 word2 中前 j 个字符即位置为 j - 1 的字符所需要的最少操作数 # 如果此时 word1 中第 i - 1 个位置的字符(即当前遍历 i 时的字符) # 与 word2 中第 j - 1 个位置的字符【相等】(即当前遍历 j 时的字符) # 说明 word1 的前 i - 1 个字符转换为 word2 中前 j - 1 个字符成功后, # word1 的前 i 个字符也就成功转换为 word2 的前 j 个字符了 if word1[i - 1] == word2[j - 1] : # dp[i][j] 表示 word1 的前 i 个字符转换为 word2 的前 j 个字符最少操作数 # dp[i - 1][j - 1] 表示 word1 的前 i - 1 个字符转换为 word2 的前 j -1 个字符最少操作数 # 比如 word1 为 abcd,word2 为 efgd # 此时 i = 4,j = 4 # 由于 word1.charAt(4 - 1) = d # word2.charAt(4 - 1) = d # 所以如果知道了 abc 转换为 efg 的最少操作数,那么结果也就出来了 # d --> d 不需要执行任何操作 dp[i][j] = dp[i - 1][j - 1] # 否则,说明 word1 中第 i - 1 个位置的字符(即当前遍历 i 时的字符) # 与 word2 中第 j - 1 个位置的字符【不相等】(即当前遍历 j 时的字符) else: # 那么 dp[i][j] 可以从以下三种状态转换过来 # 1、word1(L1-1) ——> word2(L2-1) # 2、word1(L1-1) ——> word2(L2) # 3、word1(L1) ——> word2(L2-1) # word1(L1-1) ——> word2(L2-1) 的最少操作数是 dp[i - 1][j - 1] # word1(L1-1) ——> word2(L2) 的最少操作数是 dp[i - 1][j] # word1(L1) ——> word2(L2-1) 的最少操作数是 dp[i][j - 1]) # 选这三种状态的较小值后, # 1、如果是 word1(L1-1) ——> word2(L2-1) 过来的,再执行 1 次替换操作 # 2、如果是 word1(L1-1) ——> word2(L2) 过来的,再执行 1 次删除操作 # 3、如果是 word1(L1) ——> word2(L2-1) 过来的,再执行 1 次插入操作 dp[i][j] = min(dp[i - 1][j - 1],min(dp[i - 1][j],dp[i][j - 1])) + 1 # dp[L1][L2] 表示 word1 的前 L1 个字符转换为 word2 的前 L2 个字符最少操作数 # 把这个结果进行返回 return dp[L1][L2]