题目
给定一个源串S和目标串T,能够对源串进行如下操作:
1.在给定位置上插入一个字符
2.替换任意字符
3.删除任意字符
写一个程序,返回最小操作数,使得对源串进行这些操作后等于目标串。
举例
ab -->abc :1
ab -->ac :1
good -->god :1
小技巧
删除和插入都可以理解为添加一个占位符,比如:
S :ahi
T : _hi
即删除S中的a
S : _ello
T : hello
即在S中插入一个h
dp思路
dp[i][j] 表示S的前i个位置 和 T的前j个位置对齐的最少得分
那么就要寻找该状态的前一个状态 以导出递推关系
dp[i-1][j-1] +same(i,j) 本轮对齐操作(比对)的位置就是S的 i 和 T的 j(认为i 和 j对齐),如果二者不同则same为1(替换操作发生)。 因此前一个状态是dp[i-1][j-1],i-1 和 j-1 对齐。
dp[i-1][j] +1 删除S的 i ,相当于T中添加占位符与i对齐 本轮发生对齐操作的只有S 的 i,因此前一个状态是dp[i-1][j] 。删除S的 i也可以理解为此时只有S的 i是多余的,也就是S的前i-1 和 T 的前 j是对齐的。
dp[i][j-1] +1 S中插入占位符与T 的 j对齐,插入操作。 本轮发生对齐操作的只有T 的 j,因此前一个状态是dp[i][j-1] 。也可以理解为此时只有T的 j是多余的,也就是S的前 i 和 T 的前 j-1是对齐的。
综上:
dp[i][j] = min(dp[i-1][j-1] +same(i,j),dp[i-1][j] +1,dp[i][j-1] +1)
初值:
Dp[0][j] = j S 是空的 T有几位 就插入几位
Dp[i][0] = i T 是空的 S有几位 就删除几位
时空复杂度:
依次填充二维矩阵嘛 O(m*n) ,m,n是各自长度
代码
int minDistance(string word1, string word2) { int m = word1.size(); int n = word2.size(); vector<vector<int>> dp(m+1,vector<int>(n+1));//开二维数组 注意加一 for(int i=0;i<=m;++i){ for(int j=0;j<=n;++j){ if(i==0){ dp[i][j] = j; } else if(j==0){ dp[i][j] = i; } else{ //dp数组中的下标k 对应于字符串word中的下标k-1 dp[i][j] = min(dp[i-1][j-1]+((word1[i-1]==word2[j-1])?0:1),min(dp[i][j-1]+1,dp[i-1][j]+1)); } } } return dp[m][n]; }
空间优化:节省一个维度
二维 变一维:
Dp[i][j-1] -> dp[j-1]
Dp[i-1][j] -> dp[j]
int minDistance(string word1, string word2) { int m = word1.size(); int n = word2.size(); vector<int> dp(n+1); for(int i=0;i<=m;++i){ int last; for(int j=0;j<=n;++j){ if(i==0){ dp[j] = j; } else if(j==0){ last = dp[j]; dp[j] = i; //dp[i][0] 因此last:dp[i-1][0] 当下一轮j变成1时使用该last } else{ int temp = dp[j]; //更新之后的dp[j]就是原来的dp[i][j] 未更新的dp[j]就是dp[i-1][j] dp[j-1]就是dp[i][j-1] //last 代替dp[i-1][j-1] 通过dp[j](原dp[i-1][j])延时一步得到 dp[j] = min(last+((word1[i-1]==word2[j-1])?0:1),min(dp[j-1]+1,dp[j]+1)); last = temp; } } } return dp[n]; }