一、问题
给定一个源串和目标串,能够对源串进行如下操作:
在任意位置上插入一个字符;
替换任意字符;
删除任意字符。
写一个程序,实现返回最小操作次数,使得对源串进行上述这些操作后等于目标串(源串和目标串的长度都小于2000)
二、解析
假设目标串为s1,源串为s2,如果把源串s2的每个字符一个个对照目标串s1进行操作拟合,若 认 为s1的第i个字符为s1[i],s2的第j个字符为s2[j],那么随着i和j的分别推移,可以把思路分成以下几种情况:
- 如果s1[i] == s2[j]:
即当前遍历到的s1的字符与s2的字符相等,那么说明源串不需要进行修改就和目标串匹配了,只要直接各自找下一个字符进行比较即可,即i,j各+1向后移动; - 如果s1[i] != s2[j]:
那么有三种操作,即在源串当前位置插入和目标串一样的字符,删除源串当前的字符,或者把当前位置的字符替换成和目标串一样的字符;
可以用动态规划的思路去分析其中的子问题关系,定义dis[i][j]表示当遍历到s1的第i个字符,s2的第j个字符时,使s1前i个字符与s2前j个字符相等的最短步数。假设dis[i-1][j-1]已经是最优情况,此时,为了让dis[i][j]符合要求成为最佳情况,就需要考虑在dis[i-1][j-1]进行怎样的操作能得到dis[i][j],按照之前的分析,有四种可能性,即不变、替换、插入、删除;
不变:
即当s1[i] == s2[j]时,因为不需要进行任何操作,所以dis[i][j]= dis[i-1][j-1],直接下一步即可;
替换:当s1[i] != s2[j]时, 可以把s2当前的字符s2[j]替换成s1[i],让他们相等,此时dis[i][j]= dis[i-1][j-1]+1,即在dis[i-1][j-1]的基础上步数+1即可;
插入:
当s1[i] != s2[j]时,可以在s2当前的字符s2[j]前插入一个s1[i]字符,让原本的s2[j]后移,而插入后的s2[j]位置与s1[i]相等,这相当于在dis[i-1][j]的基础上操作了一步,因为此时s1[i]还不等于s2[j],dis[i-1][j]记录了遍历到s1[i]前一位的最佳步数,只有进行了这一步插入操作之后s2[j]才会等于s1[i],所以用的是dis[i-1][j]。所以这种情况dis[i][j]= dis[i-1][j]+1;
删除:
当s1[i] != s2[j]时,可以在s2当前的字符s2[j]删除,让原本的s2[j+1]前移到s2[j],这相当于在dis[i][j-1]的基础上操作了一步,所以这种情况dis[i][j]= dis[i][j-1]+1;
只需要取上述四步中最小的结果就能保证子问题情况最小了,其中如果s1[i]==s2[j]不操作一定是最小的,剩下三个取min即可;
所以状态转移方程如下:
三、设计
核心DP代码:
for (int i = 0; i <= len1;i++){
for (int j = 0; j <= len2;j++){
//填写初始状态
if(i==0){
dis