题目链接
思考:刚开始想的时候想成了区间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];
    }
};