给定两个单词word1和word2,请计算将word1转换为word2至少需要多少步操作。
你可以对一个单词执行以下3种操作:
a)在单词中插入一个字符
b)删除单词中的一个字符
c)替换单词中的一个字符
你可以对一个单词执行以下3种操作:
a)在单词中插入一个字符
b)删除单词中的一个字符
c)替换单词中的一个字符
public class Solution { /** * 题目解析: * 1. 本题涉及动态规划 - Dynamic Programming * 之所以翻译成动态规划,是因为发明者是数学家,Programming不是CS中的编程,应该理解为规划或计划 * 该解释参考自算法设计与分析基础》 * 2. 给定两个单词word1和word2,需要将word1转换为word2 * 转换的条件是只能执行:插入一个字符,删除一个字符,替换一个字符 * 这三种操作,没执行一次,算一步,求word1转换为word2所需最小步数 * * 解题思路: * 1. 动态规划解题思路为: 拆分为子问题 - 找出递归式 * 2. 动态规划 需要用最优解来填充一张表 - 可能是一维表 or 二维表 * * 问题举例理解: “ab” 到 “abc” 的最小步数 * 所有子问题: * 0. "" 到 "a"【及 到 "ab" 及 到 "abc"】的最优解 * 1. "a" 到 "a" 【及 到 "ab" 及 到 "abc"】的最优解 * 2. "ab" 到 "a" 【及 到 "ab" 及 到 "abc"】的最优解 * 用这些最优解填充一张二维表 表的右下角为 整个问题的"ab" 到 "abc"的最优解 * 二维表:#代表空字符串 * 0 # a b c * # 0 1 2 3 * a 1 0 1 2 * b 2 1 0 1 * 由表可知 由"ab" 到 "abc" 最优解:只需进行一步 * * 递推公式: * F(i,j) 代表word1的前i -1个字符组成的字符串到 word2的前j -1个字符组成的字符串的最优解 * 例:F(2, 3) 代表word1的前1个字符组成的字符串到 word2的前2个字符组成的字符串的最优解 * 若 i == j,则意为着不需额外操作,则F(i,j) 显然 等于 F(i - 1,j - 1) * 若 i != j,则肯定需要1步操作来转换 * 以 "ab" 到 "abc"为例,该最优解为: min{"a" 到 "abc"的最优解, "ab" 到 "ab"的最优解,"a" 到 "ab" 的最优解 } + 1 * 所以 该情况递推公式为:F(i,j) = min{F(i - 1, j), F(i, j - 1),F(i - 1, j - 1) } + 1 * */ public int minDistance(String word1, String word2) { // 获取字符串长度 以便建立二维表 int len1 = word1.length(); int len2 = word2.length(); // 建立dp表 int[][] dp = new int[len1 + 1][len2 + 1]; // 初始化表 // 第一行为 空字符串 到 对应字符串 所需 转换步数 for (int i = 0; i <= len2; i++) { dp[0][i] = i; } // 第一列为 空字符串 到 对应字符串 所需 转换步数 for (int i = 0; i <= len1; i++) { dp[i][0] = i; } // 比较 字符 填充dp表其余部分 // 跳过第一行第一列:因为已经初始化填充了 for (int i = 1; i <= len1; i++) { // word1 for (int j = 1; j <= len2; j++) { // word2 // i 索引对应值== j 索引对应值,F(i, j ) = F(i - 1, j - 1) if (word1.charAt(i - 1) == word2.charAt(j - 1)) { dp[i][j] = dp[i - 1][j - 1]; } else { // 其他情况 // 取min{F(i - 1, j - 1) F(i - 1, j) F(i, j - 1) } + 1 int temp = Math.min(dp[i - 1][j] , dp[i][j - 1]); dp[i][j] = Math.min(temp, dp[i - 1][j - 1]) + 1; } } } return dp[len1][len2]; } }
public class Solution { public int minDistance(String word1, String word2) { if(word1 == null && word2 == null) return 0; if(word1 == null) return word2.length(); if(word2 == null) return word1.length(); // dp[i][j]代表由word1的前i个子串变为word2的前j个子串的花费 // 其中i,j均可为0,代表空串:"" int[][] dp = new int[word1.length() + 1][word2.length() + 2]; dp[0][0] = 0; // 首先初始化两个子串中有一个为空串的情况 for(int i = 1; i <= word1.length(); i++){ dp[i][0] = i; } for(int j = 1; j <= word2.length(); j++){ dp[0][j] = j; } for(int i = 1; i <= word1.length(); i++){ for(int j = 1; j <= word2.length(); j++){ // 如果这两个字符相等,那么交由上一种情况处理,如abc,dfc // 这种情况与ab,df花费是一样的 // 不然就要在删除,插入,修改中取花费最小的那个 if(word1.charAt(i - 1) == word2.charAt(j - 1)) dp[i][j] = dp[i-1][j-1]; else dp[i][j] = Math.min(Math.min(dp[i-1][j], dp[i][j-1]), dp[i-1][j-1]) + 1; } } return dp[word1.length()][word2.length()]; } }
public class Solution { public int minDistance(String word1, String word2) { int[][] dp = new int[word1.length() + 1][word2.length() + 1]; for (int i = 0; i < dp[0].length; i ++ ) dp[0][i] = i; for (int i = 0; i < dp.length; i ++ ) dp[i][0] = i; for (int i = 1; i < dp.length; i ++ ) { for (int j = 1; j < dp[0].length; j ++ ) { if(word1.charAt(i - 1) == word2.charAt(j - 1)) dp[i][j] = dp[i - 1][j - 1]; else dp[i][j] = Math.min(Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1), dp[i - 1][j - 1] + 1); } } return dp[word1.length()][word2.length()]; } }
class Solution { public: int minDistance(string word1, string word2) { int m = word1.length(); int n = word2.length(); vector<vector<int> > dp(m+1,vector<int>(n+1,0)); for(int i=1;i<=m;i++) dp[i][0] = i; for(int j=1;j<=n;j++) dp[0][j] = j; for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) { if(word1[i-1] == word2[j-1]) dp[i][j] = dp[i-1][j-1]; else dp[i][j] = min(dp[i-1][j]+1, min(dp[i][j-1]+1, dp[i-1][j-1]+1)); } return dp[m][n]; } };
class Solution { public: int minDistance(string word1, string word2) { //return dfs(word1, word2, 0); // 非递归动态规划法: // word2 = [a, b, d], word1 = [a, b] // 则假设我们已经求出了word1的[0,0]位置的最小操作,那么当新增一个字符b时,有如下的计算匹配: // 第一轮:令i = 1, j = 0, 使word1[i]依次匹配word2的[0, 2]位置的字符 // [a, b, c] // [a, b] // 则此时的计算结果就为 dp[1][0] = 1(删除word1中的a字符) + 1(替换单词b) // 第二轮:令i = 1, j = 1, 使word1[i]依次匹配word2的[1, 2]位置的字符 // [a, b, c] // [a, b] // 则此时的计算结果为dp[1][1] = dp[0][0] + 0(b字符已经相同无须变换) // 第三轮:令i = 1, j = 2,使word1[i]依次匹配word2的[2, 2]位置的字符 // [a, b, c] // [a, b] // 刚此时的计算结果为dp[1][2] = dp[0][1] + 1(替换单词b), // 但还需要跟上一轮的结果作比较,因为此时的结果有可能小于在上一轮匹配位置之后,直接插入第j个字符后的结果, // dp[1][2] = min(dp[1][2], dp[1][1] + 1) // 或是小于没有添加当前字符时的匹配结果 // dp[1][2] = min(dp[1][2], dp[0][2] + 1) // 最终得到一张二维表: // # "" a b c // "" 0 1 2 3 // a 1 0 1 2 // b 2 2 0 1 // 基本的流程就如上所示,但还需要确认一下初始条件和边界条件 // 初始条件: // y轴为word1,x轴为word2,由于在比较的过程中需要统计删除word1的前[0, i]字符的步数,因为在横纵坐标的 // 初始位置添加空的字符串便于计算 // 边界条件: // word1.length() == 0 || word2.length() == 0 // int w1 = word1.length(), w2 = word2.length(); if (w1 == 0) return w2; if (w2 == 0) return w1; int dp[w1+1][w2+1]; for (int i = 0; i <= w1; i++) dp[i][0] = i; for (int i = 0; i <= w2; i++) dp[0][i] = i; for (int i = 1; i <= w1; i++) { for (int j = 1; j <= w2; j++) { int cur_steps = dp[i-1][j-1] + (word1[i-1] != word2[j-1]); // 替换操作 dp[i][j] = min(cur_steps, dp[i][j-1] + 1); // 插入操作 dp[i][j] = min(dp[i][j], dp[i-1][j] + 1); // 删除操作 } } return dp[w1][w2]; }
public class Solution { public int maxValue=1000000000; public int search (int [][]f,String word1, String word2,int x,int y){ if(x > word1.length() || y >word2.length()){ return maxValue; } if(x == word1.length() && y == word2.length()){ return 0; } if(f[x][y]>0){ return f[x][y]; } if(x<word1.length()&& y<word2.length()&& word1.charAt(x) == word2.charAt(y)){ return f[x][y] = search(f,word1,word2,x+1,y+1); }else { return f[x][y] = Math.min(search(f,word1,word2,x,y+1),Math.min(search(f,word1,word2,x+1,y),search(f,word1,word2,x+1,y+1)))+1; } } public int minDistance(String word1, String word2) { if(word1==null || word2 == null) return 0; if(word1.length() ==0 && word2.length()==0) return 0; if(word1.length() ==0 ) return word2.length(); if(word2.length() ==0 ) return word1.length(); int f[][] = new int [word1.length()+1][word2.length()+1]; return search(f,word1,word2,0,0); } }
public class Solution { public int minDistance(String word1, String word2) { int row = word1.length(); int column = word2.length(); int[][] distance = new int[row+1][column+1]; for (int i = 0; i < column + 1; i++) distance[0][i] = i; for (int i = 0; i < row + 1; i++) distance[i][0] = i; for (int i = 1; i < row + 1; i++) { for (int j = 1; j < column + 1; j++) { if (word1.charAt(i-1) == word2.charAt(j-1)) { distance[i][j] = distance[i-1][j-1]; } else { int temp = distance[i-1][j-1] < distance[i][j-1] ? distance[i-1][j-1] : distance[i][j-1]; int min = temp < distance[i-1][j] ? temp : distance[i-1][j]; distance[i][j] = min + 1; } } } return distance[row][column]; } }
对应三种字符操作方式:
edit[i-1][j]+1相当于给word2的最后插入了word1的最后的字符,插入操作使得edit+1,之后计算edit[i-1][j];
edit[i][j-1]+1相当于将word2的最后字符删除,删除操作edit+1,之后计算edit[i][j-1];
edit[i-1][j-1]+flag相当于通过将word2的最后一个字符替换为word1的最后一个字符。flag标记替换的有效次数。
//动态规划ok
class Solution {
public:
int minDistance(string word1, string word2)
{
int l1=word1.length();
int l2=word2.length();
if (l1 == 0 || l2 == 0)
return max(l1, l2);
vector<vector<int>> co(l1+1,vector(l2+1,INT_MAX));
for(int i=0;i<=l1;i++)
co[i][0]=i;
for(int j=0;j<=l2;j++)
co[0][j]=j;
for(int i=1;i<=l1;i++)
{
for(int j=1;j<=l2;j++)
{
co[i][j]=co[i-1][j-1]+1; //replace
if(word1[i-1]==word2[j-1])
co[i][j]--; //don't need replacement
co[i][j]=min(co[i][j],co[i][j-1]+1); //delete
co[i][j]=min(co[i][j],co[i-1][j]+1); //insert
}
}
return co[l1][l2];
}
};
//超时递归
class Solution {
public:
int mini;
void core(string w1,string w2,int idx1,int idx2,int l1,int l2,int cnt)
{
if(cnt>mini)
return;
if(idx1>=l1||idx2>=l2)
{
if(idx2<l2)
cnt+=l2-idx2;
else if(idx1<l1)
cnt+=l1-idx1;
mini=min(mini,cnt);
return;
}
if(w1[idx1]==w2[idx2])
core(w1,w2,idx1+1,idx2+1,l1,l2,cnt);
else
core(w1,w2,idx1+1,idx2+1,l1,l2,cnt+1);//replace
core(w1,w2,idx1,idx2+1,l1,l2,cnt+1);//insert
core(w1,w2,idx1+1,idx2,l1,l2,cnt+1);//delete
}
int minDistance(string word1, string word2)
{
int l1=word1.length();
int l2=word2.length();
if(l1==0||l2==0)
return max(l1,l2);
mini=INT_MAX;
core(word1,word2,0,0,l1,l2,0);
return mini;
}
};
public class Solution { public int minDistance(String word1, String word2) { char[] chs = word1.toCharArray() ; char[] cht = word2.toCharArray() ; int n = chs.length ; int m = cht.length ; if(n == 0 || m == 0){ return Math.abs(n-m) ; } int[][] dp = new int[chs.length][cht.length] ; for(int i = 0;i < n;i++){ for(int j = 0;j < m;j++){ dp[i][j] = n+m ; } } boolean flag = false ; for(int i = 0;i < n;i++){ if(chs[i] == cht[0]){ flag = true ; } dp[i][0] = flag ? i:i+1 ; } flag = false ; for(int j = 0;j < m;j++){ if(chs[0] == cht[j]){ flag = true ; } dp[0][j] = flag ? j:j+1 ; } for(int i = 1;i < n;i++){ for(int j = 1;j < m;j++){ int num = chs[i] == cht[j] ? 0 : 1 ; dp[i][j] = Math.min(dp[i-1][j]+1,dp[i][j]) ; dp[i][j] = Math.min(dp[i][j-1]+1,dp[i][j]) ; dp[i][j] = Math.min(dp[i-1][j-1]+num,dp[i][j]) ; } } return dp[n-1][m-1] ; } }
class Solution { public: int minDistance(string word1, string word2) { int len1=word1.size(),len2=word2.size(); vector<vector<int>> dp(len1+1,vector<int>(len2+1)); for(int i=0;i<=len1;++i) dp[i][0]=i; for(int j=0;j<=len2;++j) dp[0][j]=j; for(int i=1;i<=len1;++i){ for(int j=1;j<=len2;++j){ dp[i][j]=min(min(dp[i-1][j]+1,dp[i][j-1]+1),dp[i-1][j-1]+(word1[i-1]==word2[j-1]?0:1)); } } return dp[len1][len2]; } };
public int minDistance(String word1, String word2) { if (word1 == null || word2 == null) return 0; int[][] res = new int[word1.length() + 1][word2.length() + 1]; for (int i = 0; i <= word1.length(); i++) { res[i][0] = i; } for (int j = 0; j <= word2.length(); j++) { res[0][j] = j; } for (int i = 0; i < word1.length(); i++) { for (int j = 0; j < word2.length(); j++) { if (word1.charAt(i) == word2.charAt(j)) res[i + 1][j + 1] = res[i][j]; else { res[i + 1][j + 1] = 1 + min(res[i + 1][j], res[i][j + 1], res[i][j]); } } } return res[word1.length()][word2.length()]; } private int min(int i, int j, int k) { int min = Math.min(i, j); min = Math.min(min, k); return min; }
int minDistance(string word1, string word2) { int l1 = word1.size(), l2 = word2.size(); if(l1 == 0) return l2; if(l2 == 0) return l1; vector<vector<int>> dp(l1+1, vector<int>(l2+1, 0)); for(int i = 0; i <= l1; ++i) dp[i][0] = i; for(int j = 0; j <= l2; ++j) dp[0][j] = j; for(int m = 1; m <= l1; ++m){ for(int n = 1; n <= l2; ++n){ if(word1[m-1] == word2[n-1]) dp[m][n] = dp[m-1][n-1]; else{ int min1 = min(dp[m-1][n]+1, dp[m][n-1]+1); dp[m][n] = min(min1, dp[m-1][n-1]+1); } } } return dp[l1][l2]; }
class Solution { public: // dp[i][j]代表由word1的前i个子串变为word2的前j个子串的花费 int minDistance(string word1, string word2) { int m = word1.length(),n = word2.length(); vector<vector<int> > dp(m+1,vector<int>(n+1,0)); for(int i=1;i<=m;i++) dp[i][0] = i; /// delete i chars of word1 for(int j=1;j<=n;j++) dp[0][j] = j; /// insert j chars of word2 for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { if(word1[i-1] == word2[j-1]) dp[i][j] = dp[i-1][j-1]; else dp[i][j] = min(dp[i-1][j]+1,min(dp[i][j-1]+1,dp[i-1][j-1]+1)); /// delete,insert,replace } } return dp[m][n]; } };
class Solution { public: int minDistance(string word1, string word2) { int m = word1.size(); int n = word2.size(); vector<vector<int> >dp(m+1,vector<int>(n+1)); for(int i = 0;i <= m ;i++){ for(int j = 0; j <= n ;j++){ if(i ==0){ dp[i][j] = j; } else if(j ==0){ dp[i][j] = i; } else{ dp[i][j] = min(dp[i-1][j]+1,min(dp[i][j-1]+1,dp[i-1][j-1]+(word1[i-1]==word2[j-1]?0:1))); } } } return dp[m][n]; } };
int minDistance(string word1, string word2) { // write code here int m = word1.length(); int n = word2.length(); vector<vector<int>> dp(m+1,vector<int>(n+1)); //设置边界 for(int i=1;i<=m;++i)dp[i][0]=i; for(int i=1;i<=n;++i)dp[0][i]=i; for(int i=1;i<=m;++i) { for(int j=1;j<=n;++j) { int n = 1; if(word1[i-1]==word2[j-1]) n = 0; dp[i][j] = min(dp[i-1][j-1]+n,min(dp[i-1][j],dp[i][j-1])+1); } } return dp[m][n]; } };