题目链接
思考:刚开始想的时候想成了区间dp,思考相同长度下不同位置的字符串的匹配,大概的想法是用\(dp[len][i][j]\)来表示长度为len的A串从i开始的子串变成B串从位置j开始所需要的最小代价,后面发现操作数有三种,很难用区间的方式来维护,毕竟修改完全可以不在两端,而且复杂度也是在\(O(10^8)\)不可能接受,所以作罢。
正解:用\(dp[i][j]\)表示A从\([1...i]\)的子串变成B从\([1...j]\)所需要的最小代价,分析三种操作数
- 在A的末尾增加一个元素,此时代价为\(dp[i][j-1]+1\)
- 在B的末尾增加一个元素,此时代价为\(dp[i-1][j]+1\)
- 将A的末尾元素进行修改,如果不用修改,也就是\(A_i= B_j\)时,此时A的末尾元素不同进行修改得
\[dp[i][j]=\left\{\begin{array}{rcl} dp[i-1][j-1] &{A_i=B_j}\\ dp[i-1][j-1]+1 &{A_i \neq B_j}\end{array}\right.
\]
初始状态
\[ dp[i][j]=
\begin{cases}
dp[0][j]=j\\
dp[i][0]=i
\end{cases} \]
为什么从左到右推进是可以的呢,首先我们可以想象一个位置\(A_i\)假设它应该匹配上的是一个后面的\(B_j\),那么它只要继承处于i之前的\(dp[j-1]\)的值就行了,所以前段对后段没有影响
class Solution {
public:
int minDistance(string word1, string word2) {
int n=word1.size(),m=word2.size();
int dp[n+1][m+1];
memset(dp,0,sizeof dp);
for(int i=0;i<=n;i++) dp[i][0]=i;
for(int j=0;j<=m;j++) dp[0][j]=j;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int l=dp[i][j-1]+1; //这里表示A+1
int r=dp[i-1][j]+1; //这里表示B+1;
int c=dp[i-1][j-1]+(word1[i-1]!=word2[j-1]);
dp[i][j]=min(l,min(r,c));
}
}
return dp[n][m];
}
};