动态规划问题
1.LeetCode 72 编辑距离(简单易理解),思路,步骤和代码实现
目标:找到单词1转变为单词2的最少步数。
//例如:
//word1="horse" word2="ros"
//则输出最少步数为:3
//horse->hors
//hors->hos
//hos->ros
思路:不断检测词尾是否一致,原因是词尾一致后dp[i] [j] = dp[i-1] [j-1],例如hors转变为ros的最小步数一定等于hor转变为ro的步数。
-
定义二维数组dp[i] [j],其意义为:长度为i的word1转变为长度为j的word2所需的最小步数,这一步对于所有动态规划问题都很重要,至于为什么选择的是二维数组以及为什么根据单词长度来定义,一方面是经验,另一方面因为处理的是两个单词,分别记录检测词尾。
-
对各种情况进行分类,在动态规划问题中,基本都需要找到dp[i] [j]与dp[i-1] [j],dp[i] [j-1]以及dp[i-1] [j-1]之间的关系。
-
词尾相同时
词尾相同时显然有dp[i] [j]=dp[i-1] [j-1],例如从hors–>hos的最小步骤等于hor–>ho的最小步骤,这里就体现了检测词尾的另一个好处,i和j在变换的同时,其对应的字母正好就是当前的词尾,例如:hors–>hos,由于词尾相同,则dp[4] [3]=dp[3] [2],word1=[,h,o,r,s],word2=[,h,o,s],word1[4] = word2[3],故i–.j–,dp不变,下一步计算hor–>ho,对比的就是word1[3]和word2[2]。
-
词尾不同时,可执行:
- 增:例如hos–>rose,则需要在word1最后加个e,即dp[i] [j] = dp[i] [j-1] + 1,可以理解为:在hos–>ros的基础上再把word1词尾加了一个e;
- 改:例如hos–>ror,则需要将s改为r,即dp[i] [j] = dp[i-1] [j-1] + 1,可以理解为:在ho–>ro的基础上需要加一步将s–>r的步骤;
- 删:例如hors–>ror,则需要删掉s,即dp[i] [j] = dp[i-1] [j] + 1,可以理解为:在hor–>ror的基础上需要加一步将s删掉的步骤;
-
-
定义初始条件:设想一下,再不断计算后,总会出现一个i或j变为0,这时候再减就成负数了,故当某一个为0时就是初始条件,因此需要定义dp[0] [m] = m(一个空字符串想变成m个字符的字符串显然是不断增),同理dp[k] [0] = k,但也可根据上文中的词尾不同执行的增改删,当i–成为负数时不执行,只需定义dp[0] [0] =0。
-
综上可知:由于定义的dp是最小步数,因此当词尾相同时:dp[i] [j]=dp[i-1] [j-1],当词尾不同时:dp[i] [j] = min{增,删,改}+1,即dp[i] [j] = math.min(dp[i] [j-1],dp[i-1] [j-1],dp[i-1] [j])+1。
实现代码(C++):
#include <iostream>
using namespace std;
int min1(int i,int j,int k)
{
int a;
if (i <= j)
a = i;
else a = j;
if (a <= k)
return a;
else return k;
}
int main()
{
string word1,word2;
word1 = "horse";
word2 = "ros";
int m = word1.length();
int k = word2.length();
int dp[500][500];
for (int i = 0; i <= k; i++) dp[0][i] = i;
for (int j = 0; j <= m; j++) dp[j][0] = j;
for (int i = 1; i <= m; i++)
for(int j=1;j<=k;j++)
{
if (word1[i-1] == word2[j-1])
dp[i][j] = dp[i - 1][j - 1];
else
dp[i][j] = min1(dp[i-1][j],dp[i-1][j-1],dp[i][j-1])+1;
}
cout <<"最少步数为:" << dp[m][k];
return 0;
}