【leetcode】Edit Distance 详解

题目

给定两个字符串word1,word2,将word2变成word1所需的最小步数。规定只能用以下三种操作进行修改,且每种操作的步数都计为1.

op1:插入一个字符

op2:删除一个字符

op3:替换一个字符

例:

word1 = word, word2 = wore, 则将wore的e替换为d则可。

word1 = word,word2 =  wor,则在wor的后面插入一个d则可。

word1 = word, word2 = wordd,则删除wordd后面的d则可。

这里只是简单的说明下这三种操作,算法中考虑的东西比这复杂,但是基本的操作是如此。

/*********************************************************************************************************************************************************/

分析

在leetcode里我们见过很多类似模式的题,其模式可简化为:给定两个串str1,str2,按某一规则,从str1得到str2.这类问题基本都是用动态规划来解决。既然是动态规划,我们来具体分析一下。

我们用d(s1,s2)来表示s1和s2的”距离“。

1) d('','') = 0,'' 表示空串

2) d(s,'') = d('', s) =  |s| , s 表示 s 的长度

3) d(s1 + ch1,s2 + ch2) 

=min( d(s1, s2) + ch1== ch2 ? 0 : 1,

           d(s1 + ch1, s2) + 1,

           d(s1, s2 + ch2) + 1)

第一种情况好理解,都是空的时候不需要任何操作,二者相等。

第二种情况也好理解,若s1是空,只需要将s2中的字符全部删除,需要的操作步数是s2的长度;若s2是空,则只需要在s2中插入s1对应的字符,操作步数为s1的长度。

第三种情况,若两者都不为空,那么这两个字符串肯定都有最后一个字符,我们分别用ch1和ch2来表示,我们需要做的就是将s1 + ch1 变成 s2 + ch2,对于最后一个字符:

     a)若ch1 和 ch2 相同,那么这一步我们不用进行修改,只需要将s1变成s2就可以了。这样,整体的操作步数为 d(s1,s2)

     b)若ch1 和 ch2 不同,我们有三种修改方式:

         i)将ch1 替换为 ch2,那么剩下的事情就是将 s1 变成 s2,整体的操作步数为 d(s1, s2) + 1。

         ii)将ch1 删除,那么剩下的事情就是将 s1 变成 s2 + ch2,整体的操作步数为 d(s1, s2 + ch2) + 1

         iii)在s1 + ch1 后面插入ch2,ch2就匹配了,那么剩下的事情就是将 s1 + ch1 变成 s2,整体操作步数为d(s1 + ch1,s2) + 1。

这样理完思路,我们写出动态规划的每个部分:

状态:m[ i,j] = d(s1[1..i ],s2[1,j ])

初始条件:m[0,0] = 0

                   m[ i,0] = i

                   m[ 0 ,j ] = j

状态转移方程:m[i, j ] = min( m[i - 1, j - 1] + s1[ i - 1] == s2[ j - 1] ? 0 : 1  // replace or not

                                                   m[ i - 1, j ] + 1, //delete

                                                   m[ i, j - 1]  + 1, //insert

最终结果:m[ |s1|, |s2| ]。

例:

【leetcode】Edit Distance 详解_第1张图片

通过状态转移方程,我们看到当前行的操作只用到当前行和前一行,所以可以不用定义一个|s1| * | s2 | 大小的表。

实现:

//code

/*
1)state:
dp[i,j]--word1[1..i],word2[1..j], the minimum ops we have done.

2)state trans function: 
dp[i, j] = min(dp[i - 1, j - 1] + (word1[i] == word2[j] ? 0 : 1),//replace or do nothing
              dp[i-1,j] + 1,//delete the current word in word1
			  dp[i, j - 1] + 1)//insert the same word as word2[j - 1] into word1.
3)initial: 
dp[0,0] = 0
dp[i,0] = i
dp[0,j] = j

4)final state:
  dp[|word1|,|word2|]
*/
int minDistance(string word1, string word2) {
	//equal do nothing
	if( 0 == word1.size() && 0 == word2.size())
		return 0;
	//delete all the words in word2
	else if(word1.size() == 0)
		return word2.size();
	//insert all the words of word1 into word2
	else if(word2.size() == 0)
		return word1.size();
	int len_word1 = word1.size();
	int len_word2 = word2.size();
	vector<int> dp_pre(len_word1 + 1, 0);
	//initial
	for (int i = 0; i <= len_word1; ++i)
		dp_pre[i] = i;
	vector<int> dp_cur(len_word1 + 1, 0);
	for (int i = 1; i <= len_word2; ++i)
	{
		dp_cur[0] = i;
		for (int j = 1; j <= len_word1; ++j)
		{
			int num_repalce = 0;
			if(word1[j - 1] != word2[i - 1])
				num_repalce = 1;
			dp_cur[j] = min(min(dp_cur[j - 1] + 1, dp_pre[j] + 1),dp_pre[j - 1] + num_repalce);
		}
		dp_pre = dp_cur;
	}

	return dp_pre[len_word1];
}

复杂度分析:

时间复杂度为O(|s1| * |s2|) ,O(n^2), 空间复杂度O(n)。

应用:

1)文件修改:文件在进行修改了以后,如果用修改后的内容完全替换修改前的内容,就很费时,我们可以通过这种方式修改最少的内容,尤其是通过网络的方式。

2)远程桌面:A主机远程B主机,那么B主机的屏幕要显示在A主机的屏幕上,但是大部分时候,B主机屏幕的变化总是局部的,所以只需要在A主机上变化B主机变化的部分。

还有很多,不一一列举。

说明:

本文借鉴参考了http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Dynamic/Edit/ 这篇文章,写的很好。

欢迎转载,探讨指正,转载及其他,请说明出处。

                             

你可能感兴趣的:(LeetCode,C++,算法,面试题,动态规划)