动态规划dp函数 + 记忆数组
- 算法思路
先定义状态,匹配与不匹配两种。当不匹配时,有三种选择: 插入,删除,替换。
dp(i, j)为str1[0...i]和str2[0..j]字符串为了匹配做出的代价。
对于给定的字符串str1,str2,从右到左,即自顶向下进行匹配。会出现两种情况:- 当前字符匹配时,dp(i, j)沿用dp(i-1, j-1)的代价。
- 当前字符不匹配时,有三种选择。
dp(i, j-1) + 插入的代价
dp(i-1, j) + 删除的代价
dp(i-1, j-1) + 替换的代价
当我们每个选择在向下递归时,只有三种选择,第一个选择结束又返回,进行第二个选择时,必定存在某个选择在当前字符被递归过的情况,因此我们可以利用记忆数组来减少重复的计算。
import java.util.*; public class Solution { //记忆数组来减少递归次数 int[][] memory; /** * min edit cost * @param str1 string字符串 the string * @param str2 string字符串 the string * @param ic int整型 insert cost * @param dc int整型 delete cost * @param rc int整型 replace cost * @return int整型 */ public int minEditCost (String str1, String str2, int ic, int dc, int rc) { int len1 = str1.length(); int len2 = str2.length(); memory = new int[len1+1][len2+1]; //初始化记忆数组 for(int i = 0; i <= str1.length(); i++){ for(int j = 0; j <= str2.length(); j++){ memory[i][j] = -1; } } //自顶向下递归 return dp(str1, str2, len1, len2, ic, dc, rc); } /** * dp函数 */ public int dp(String str1, String str2, int i, int j, int ic, int dc, int rc) { //利用记忆数组直接返回 if(memory[i][j] != -1) return memory[i][j]; if(i == 0) // str1先走完,只能插入str2还没比较过的字符了 return memory[i][j] = j*ic; if(j == 0) // str2先走完, 只能删除str1还没比较过的字符了 return memory[i][j] = i*dc; if(str1.charAt(i-1) == str2.charAt(j-1)) //如果当前字符相等, 直接向前比较 memory[i][j] = dp(str1, str2, i-1, j-1, ic, dc, rc); else{ //比较哪个动作的代价最小 memory[i][j] = Math.min(Math.min(dp(str1, str2, i, j-1, ic, dc, rc)+ic, dp(str1, str2, i-1, j, ic, dc, rc)+dc), dp(str1, str2, i-1, j-1, ic, dc, rc)+rc); } return memory[i][j]; } }
- 算法复杂度
- 时间:O(2^N)。自顶向下的递归深度。
- 空间:O(N*M)。二维数组的空间。
递推 dp数组
- 算法思路
可以用递推的解法,dp数组的含义跟dp函数一样,不同的是,dp数组采用的是自底向上的解法,递归采用的是自顶向下的解法。
import java.util.*; public class Solution { /** * min edit cost * @param str1 string字符串 the string * @param str2 string字符串 the string * @param ic int整型 insert cost * @param dc int整型 delete cost * @param rc int整型 replace cost * @return int整型 */ public int minEditCost (String str1, String str2, int ic, int dc, int rc) { // write code here int n = str1.length(), m = str2.length(); int[][] dp = new int[n+1][m+1]; for(int i = 1; i <= n; i++){ dp[i][0] = i*dc; } for(int j = 1; j <= m; j++){ dp[0][j] = j*ic; } for(int i = 1; i <= n; i++){ for(int j = 1; j <= m; j++){ if(str1.charAt(i-1) == str2.charAt(j-1)) dp[i][j] = dp[i-1][j-1]; else dp[i][j] = min(dp[i-1][j]+dc, dp[i][j-1]+ic, dp[i-1][j-1]+rc); } } return dp[n][m]; } public int min(int a, int b, int c){ return Math.min(a, Math.min(b, c)); } }
- 算法复杂度
- 时间复杂度:O(N*M)。其中 m 为 str2 的长度,n 为 str1的长度。
- 空间复杂度: O(N*M)。二维数组的空间。