[每周一题]Edit Distance from LeetCode


“一个单词和另一个单词的距离有多远?”

“哪两个?life&death的距离有时候是一把菜刀。”

“。。。”


事实上,从一个单词变换到另一个单词每一步可以有三种选择。把一个字母去掉,或者改变一个字母,或者增加一个字母。

每一步,就是一步。这看起来是废话,实际上也是废话,然而最后要求的距离就是步数。

比如说let和lat差一步,lll和xxx差三步,l和lll差两步。

介绍完就开始聊聊怎么解决了。其实这题看着就是个DP的问题。大神一眼就看出来的东西,我这个渣渣还慢慢琢磨了好久。。。想直接看答案的请拉到最下面。

那么DP问题当然要找到状态。状态也比较明朗:假如两个词word1和word2,数组d[len1][len2]中,d[i][j]意味着word1中从i到len1的词和word2中从j到len2的单词的最短距离。

找到一种状态,下一步是试试看能不能找到状态转移方程。如果找不到,就重新定义别的状态。从直觉上看,这个状态还是挺靠谱的:

  • 当word1[i]==word2[j]的时候,d[i][j] = d[i+1][j+1]
  • 当word1[i]!=word2[j]的时候,d[i][j] = d[i+1][j+1] + 1

然而并不够,这好像只使用了改变一个字母的能力。仔细想一想,增加或者去掉一个字母在d中怎么体现呢?就不是对角线移动,而是平移或者竖直移动。这样:

  • d[i][j] = d[i+1][j] + 1
  • d[i][j] = d[i][j+1] + 1

什么时候该改变字母、什么时候该增删字母呢?当然是看哪种方式距离比较短,所以状态转移方程的雏形出现了:

  • d[i][j] = min(d[i+1][j] + 1, d[i][j+1] + 1, d[i+1][j+1] + word1[i] == word2[j] ? 0 : 1)

实时上可以直接从第一个字母开始比较,也就是d[i][j]表示word1中从0到i的词和word2中从0到j的词的最短距离,由于我脑子比较抽所以从后面才想起来。。

并且还抽风地从len1-1, len2-1每次计算最下面一行和最右边一列,实在是惭愧。。

事实上最后的代码可以很简单,借用leetcode上fox13的代码:

public class Solution {
private int min3 (int a, int b, int c)
{
    int min = a; if (min > b) min = b; if (min > c) min = c;
    return min;
}
public int minDistance(String word1, String word2) 
{
    int a[][] = new int[word1.length()+1][word2.length()+1];
    for (int i=0; i<=word1.length(); i++) a[i][0] = i;
    for (int j=0; j<=word2.length(); j++) a[0][j] = j;
    for (int i=1; i<=word1.length(); i++)
    {
        for (int j=1; j<=word2.length(); j++)
        {
            if (word1.charAt(i-1) == word2.charAt(j-1))
                a[i][j] = a[i-1][j-1];
            else
                a[i][j] = min3(a[i-1][j-1], a[i][j-1], a[i-1][j])+1;
        }
    }

 return a[word1.length()][word2.length()];
}
}


附一个讲解的PPT~ http://www.stanford.edu/class/cs124/lec/med.pdf


多想多练,加油~


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值