leetcode 力扣 72 编辑距离 必看懂必会

106 阅读3分钟

lc72.jpeg

动态规划
先来看dp数组的构成,把word1放在数组的左边,用行来表示每个字符,把word2放在数组的上边,用列来表示每个字符。word1word2前面都有一个"",因为dp[0][0]表示”空字符转变到空字符“需要的次数。

再来看一下dp[i][j]的含义,它表示word1的前i个字符组成的子串转换成word2的前j个字符的组成子串,需要的最少次数。

horse转变成ros,需要的操作无非是插入,删除,替换,不变动,用dp数组来表示就是:

dp[i][j]={dp[i][j1]+1,插入dp[i1][j]+1,删除dp[i1][j1]+1,替换dp[i1][j1],不变动dp[i][j] = \begin{cases} dp[i][j - 1] + 1,\,\,插入\\ dp[i - 1][j] + 1,\,\,删除\\ dp[i - 1][j - 1] + 1,\,\,替换\\ dp[i - 1][j - 1],\,\,不变动 \end{cases}

为什么可以这样表示呢?下面一一解释

插入操作(dp[i][j] = dp[i][j - 1] + 1):

lc72_2.jpeg

看第一行

  • ""转变成ros的前1个字符r,是不是需要插入r,翻译成代码等于dp[0][1] = dp[0][0] + 1+1表示当前插入这个操作
  • ""转变成ros的前2个字符ro,是不是先插入r再插入o,翻译成代码等于dp[0][2] = dp[0][1] + 1
  • ""转变成ros的前3个字符ros,是不是先插入r再插入o再插入s,翻译成代码等于dp[0][3] = dp[0][2] + 1
    可以看出插入操作是往右移动的,dp[i][j] = dp[i][j - 1] + 1成立

删除操作(dp[i][j] = dp[i - 1][j] + 1):

lc72_3.jpeg

看第一列

  • horse的前1个字符h转变成"",是不是需要删除h,翻译成代码等于dp[1][0] = dp[0][0] + 1
  • horse的前2个字符ho转变成"",是不是需要先删除h再删除o,翻译成代码等于dp[2][0] = dp[1][0] + 1
  • horse的前3个字符hor转变成"",是不是需要先删除h再删除o再删除r,翻译成代码等于dp[3][0] = dp[2][0] + 1
    可以看出删除操作是往下移动的,dp[i][j] = dp[i - 1][j] + 1成立

替换操作(dp[i][j] = dp[i - 1][j - 1] + 1)和不变动(dp[i - 1][j - 1]):

lc72_4.jpeg

观察dp[1][1],意思是将h替换为r,其实就是操作数原地+ 1'原地'也就是'不操作,不变动'的意思,所以替换操作== dp[i][j] = dp[i - 1][j - 1] + 1,不变动 == dp[i - 1][j - 1]

算法思路:

  • String转换成字符数组,比使用word1.charAt(i)效率要高
  • 如果i指向的字符 == j指向的字符,则选择不变动
  • 如果不相等,则选择插入删除替换中操作数最小的一个

完整的转变路径如下: h变成ro不变,删除rs不变,删除e

lc72_5.jpeg

public int minDistance(String word1, String word2) {
        int rows = word1.length(), columns = word2.length();
        int[][] dp = new int[rows + 1][columns + 1];
        char[] word1Array = word1.toCharArray();
        char[] word2Array = word2.toCharArray();

        for (int i = 1; i <= rows; i++) {
            dp[i][0] = i;
        }

        for (int j = 1; j <= columns; j++) {
            dp[0][j] = j;
        }

        for (int i = 1; i <= rows; i++) {
            for (int j = 1; j <= columns; j++) {
                if (word1Array[i - 1] == word2Array[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = Math.min(Math.min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
                }
            }
        }

        return dp[rows][columns];
    }