编辑距离 五种解法

百般思念 提交于 2020-03-01 08:41:32

72. 编辑距离 【困难题】【动态规划】

给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

插入一个字符,删除一个字符,替换一个字符

输入: word1 = "horse", word2 = "ros"
输出: 3
解释: 
horse -> rorse ('h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')


输入: word1 = "intention", word2 = "execution"
输出: 5
解释: 
intention -> inention (删除 't')
inention -> enention ('i' 替换为 'e')
enention -> exention ('n' 替换为 'x')
exention -> exection ('n' 替换为 'c')
exection -> execution (插入 'u')

题目讲解

解法1:非递归一

【核心思想】

  • 动态规划

【思路】

  • dp[i][j]为:当遍历到字符串word1的第i个字符,遍历到字符串word2的第j个字符时,转换所需要的最少操作次数为dp[i][j]
  • word1[i]==word2[j],那么当前不需要进行任何操作,有dp[i][j]=dp[i-1][j-1]
  • word1[i]!=word2[j]不相等,j就有三种操作:
  1. 如果把字符word1[i] 替换成与 word2[j]相等,则有dp[i][j] = dp[i-1][j-1] + 1
  2. 如果在字符串word1末尾插入一个与word2[j]相等的字符,则有dp[i][j] = dp[i][j-1] + 1
  3. 如果把字符word1[i]删除,则有dp[i][j]=dp[i-1] [j]+1
  • 那么我们应该选择一种操作,使得dp[i][j]的值最小,即有dp[i][j] = min(dp[i-1][j-1],dp[i][j-1],dp[[i-1][j]]) + 1

【代码】

public int minDistance(String word1, String word2) {
    int len1=word1.length();
    int len2=word2.length();
    int[][] dp=new int[len1+1][len2+1];
    for(int i=0;i<=len2;i++)
        dp[0][i]=i;
    for(int j=0;j<=len1;j++)
        dp[j][0]=j;
    for(int i=1;i<=len1;i++){
        for(int j=1;j<=len2;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-1][j]),dp[i][j-1])+1;
        }
    }
    return dp[len1][len2];
}

解法2:非递归二

【核心思想】

  • 将解法一的动态规划数组从二维降至一维

【代码】

 public int minDistance(String word1, String word2) {
    int len1=word1.length();
    int len2=word2.length();
    if(len1==0)
        return len2;
    if(len2==0)
        return len1;

    int[] dp=new int[len2+1];
    for(int i=0;i<=len2;i++)
        dp[i]=i;

    for(int i=1;i<=len1;i++){
        int pre=i-1;
        for(int j=1;j<=len2;j++){
            if(j==1)
                dp[j-1]=i;            
            int temp=pre;
            pre=dp[j];            
            if(word1.charAt(i-1)==word2.charAt(j-1))
                dp[j]=temp;
            else
                dp[j]=Math.min(Math.min(temp,dp[j]),dp[j-1])+1;            
        }
    }
    return dp[len2];
}

解法3:递归一

【核心思想】

  • 依旧是动态规划的思想,将其用递归的形式表示出来。有从前往后从后往前两种方式,两者思路完全一致。

【代码】

//从后往前
public int minDistance(String word1, String word2) {
    int len1=word1.length();
    int len2=word2.length();
    if(len1==0)
        return len2;
    if(len2==0)
        return len1;
    int ans=0;
    if(word1.charAt(len1-1)==word2.charAt(len2-1))
        ans=minDistance(word1.substring(0,len1-1),word2.substring(0,len2-1));
    else{
        int dis1=minDistance(word1.substring(0,len1-1),word2.substring(0,len2-1));
        int dis2=minDistance(word1.substring(0,len1),word2.substring(0,len2-1));
        int dis3=minDistance(word1.substring(0,len1-1),word2.substring(0,len2));
        ans=Math.min(Math.min(dis1,dis2),dis3)+1;
    }
    return ans;
}

//从前往后
public int minDistance2(String word1, String word2) {
    int m = word1.length();
    int n = word2.length();
    if(m==0)
        return n;
    if(n==0)
        return m;
    if(word1.charAt(0) == word2.charAt(0))
        return minDistance(word1.substring(1), word2.substring(1));
    else
        return Math.min(1 + minDistance(word1, word2.substring(1)), Math.min(1 + minDistance(word1.substring(1), word2), 1 + minDistance(word1.substring(1), word2.substring(1))));
}

【备注】

  • 递归的方法超时,是因为重复了太多比较。其改进方式如下。

解法4:递归二

【核心思想】

  • 改进版本的递归,是用一个数组来记录已经比较过的字符串。该版本能通过oj检测。

【代码】

 public int minDistance3(String word1, String word2) {
    if (word1.length() == 0) return word2.length();
    if (word2.length() == 0) return word1.length();
    int[][] dp = new int[word1.length()+1][word2.length()+1];
    return helper(word1, word2, 0, 0, dp);
}

private int helper(String s1, String s2, int i, int j, int[][] dp) {
    if (s1.length() == i) 
        return s2.length() - j;

    if (s2.length() == j) 
        return s1.length() - i;
    
    int ans = 0;

    if(dp[i][j] > 0) {
        return dp[i][j];
    }
    if (s1.charAt(i) == s2.charAt(j)) {
        ans = helper(s1, s2, i + 1, j + 1, dp);

    } else {
        int insert = helper(s1, s2, i, j + 1, dp);
        int delete = helper(s1, s2, i + 1, j, dp);
        int replace = helper(s1, s2, i + 1, j + 1, dp);
        ans = Math.min(Math.min(insert, delete), replace) + 1;
    }
    dp[i][j] = ans;
    return ans;
}

关注微信公众号“算法岗从零到无穷”,更多算法知识点告诉你。
在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!