题目:​​https://leetcode.com/problems/edit-distance/?tab=Description​

题目要求最少的变换数目把s1变成s2,有三种操作,insert,change,delete。

f(i,j)返回从s1从i位置以后的string变换到s2从j位置以后的string,最开始从两个sting的开头比较,如果一样,返回f(i+1,j+1)。否则有三种方式,如果修改,那么就返回1+f(i+1,j+1), 如果insert,那么就返回f(i, j+1),如果delete,那么就返回f(i+1,j)。return case是只要有一个到达末尾,就返回。

有一点需要注意,就是会不会出现抖动导致死循环?就是加一个减一个。。。这样。 答案是不会的,因为insert的时候或者delete的时候,总有一个string的位置时向后移动的,因此,每一次都会向return case靠近一步。

下面是代码:

public int minDistance(String s1, String s2) {
if(s1.length() == 0) return s2.length();
if(s2.length() == 0) return s1.length();
if(s1.charAt(0) == s2.charAt(0)){
return minDistance(s1.substring(1), s2.substring(1));
}else{
int deleteMin = minDistance(s1.substring(1), s2) + 1;
int changeMin = minDistance(s1.substring(1), s2.substring(1)) + 1;
int insertMin = minDistance(s1, s2.substring(1)) + 1;
return Math.min(deleteMin, Math.min(changeMin, insertMin));
}
}


这个实现只是一个子问题的实现,写好以后我就预感到会超时,果然。。。

因为重复计算了好多case。比如说s1=abc,s2=ebc,第一次我做一次change,递归处理了f(bc,bc),当然我也可以在s1里面加入一个e,然后处理abc和bc,这是又可以删除a,接着处理f(bc,bc),重复。

更直接的,每一次我们都有三种选择,那么复杂度基本是3的幂级别。下面一张图可以很好的解释:

【Leetcode】72. Edit Distance_复杂度

如何降低?老问题,带一个memo。

public int minDistance(String s1, String s2) {
int[][] memo = new int[s1.length()][s2.length()];
for(int i = 0; i < s1.length(); i++){
Arrays.fill(memo[i], -1);
}
return subMinDistance(0 ,0, s1, s2, memo);
}

private int subMinDistance(int i1, int i2, String s1, String s2, int[][] memo){
if(i1 == s1.length()) return s2.length() - i2;
if(i2 == s2.length()) return s1.length() - i1;
if(memo[i1][i2] >= 0) return memo[i1][i2];
if(s1.charAt(i1) == s2.charAt(i2)){
return subMinDistance(i1 + 1, i2 + 1, s1, s2, memo);
}else{
int deleteMin = subMinDistance(i1 + 1, i2, s1, s2, memo) + 1;
int changeMin = subMinDistance(i1 + 1, i2 + 1, s1, s2, memo) + 1;
int insertMin = subMinDistance(i1, i2 + 1, s1, s2, memo) + 1;
memo[i1][i2] = Math.min(deleteMin, Math.min(changeMin, insertMin));
return memo[i1][i2];
}
}

总结:

(1)为了分析能否通过带memo降低复杂度,可以使用子问题执行图来帮助理解。

(2)我们知道dp有两种形式,一个是自底向上,一个是自顶向下,有些情况是两种写法都可以,但是有些往往其中的一种很难写,这个与分析的过程有关。如果是dp那么应该两种都尝试,因为一种不好实现的情况下另一种没准可以。这里如果自底向上,就是从0,0开始迭代dp,其实比较难入手。

(3)解决两个字符串的问题,同时从一端开始是一个思路。