CF56D Changing a String 题解

Posted on 2024-03-16 17:35  _XOFqwq  阅读(6)  评论(0编辑  收藏  举报

双倍经验:P2758。

\(dp_{i,j}\) 表示 \(s\)\(i\) 个字符要变成 \(t\)\(j\) 个字符所需的最少移动次数。

答案即为 \(dp_{\lvert s \rvert,\lvert t \rvert}\)

显然有初始状态 \(dp_{i,0}=dp_{0,i}=i\)

因为我们只可能从添、删、替三种操作转移而来,

于是有转移方程:

  • 添:

\[dp_{i,j}=dp_{i-1,j}+1 \]

  • 删:

\[dp_{i,j}=dp_{i,j-1}+1 \]

  • 替:

\[\begin{cases} dp_{i,j}=dp_{i-1,j-1}(s_i \neq t_i)\\ dp_{i,j}=dp_{i-1,j-1}+1(s_i = t_i) \end{cases} \]

三者取 \(\min\) 即可。

关于输出,我们定义递归函数 \(\operatorname{print}(x,y)\)

边界:\(x \le 0,y \le 0\)

接着我们分别从上述三种操作中寻找路径。

但是,我们考虑到我们输出的字符一定是目标字符串 \(t\) 中的字符。

因此我们考虑将对 \(s\) 的添、删、替操作转化为对 \(t\) 的操作。

  • 添:

    \(x,y\) 满足 \(x>0,dp_{x,y}=dp_{x-1,y}+1\)

    则执行 \(\operatorname{print}(x-1,y)\),并输出 \(\texttt{DELETE} \ y+1\)\(s\) 增加第 \(x\) 字符与 \(t\) 删去第 \(y+1\) 字符等价)。

  • 删:

    \(x,y\) 满足 \(y>0,dp_{x,y}=dp_{x,y-1}+1\)

    则执行 \(\operatorname{print}(x,y-1)\),并输出 \(\texttt{INSERT} \ y\)\(s\) 删除第 \(x\) 字符与 \(t\) 添加第 \(y\) 字符等价)。

  • 替:

    \(x,y\) 满足 \(x>0,y>0,dp_{x,y}=dp_{x-1,y-1}+1\)

    则执行 \(\operatorname{print}(x-1,y-1)\),并输出 \(\texttt{REPLACE} \ y\)\(s\) 替换第 \(x\) 字符与 \(t\) 替换第 \(y\) 字符等价)。

  • 否则:

    执行 \(\operatorname{print}(x-1,y-1)\)(即退回一个字符)。

然后这题就做完了。

code
#include<bits/stdc++.h>
using namespace std;

string A,B;
int n,m,dp[2031][2031];

void print(int x,int y){
	if(x<=0&&y<=0) return;
	if(x>=1&&dp[x][y]==dp[x-1][y]+1) print(x-1,y),cout<<"DELETE "<<y+1<<'\n';
	else if(y>=1&&dp[x][y]==dp[x][y-1]+1) print(x,y-1),cout<<"INSERT "<<y<<' '<<B[y]<<'\n';
	else if(x>=1&&y>=1&&dp[x][y]==dp[x-1][y-1]+1) print(x-1,y-1),cout<<"REPLACE "<<y<<' '<<B[y]<<'\n';
	else print(x-1,y-1);
}

int main(){
    getline(cin,A); getline(cin,B);
    n=A.length(),m=B.length();
    A='#'+A,B='#'+B;
    
    for(int i=1;i<=n;i++) dp[i][0]=i;
    for(int i=1;i<=m;i++) dp[0][i]=i;
    
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            dp[i][j]=min(min(dp[i-1][j]+1,dp[i][j-1]+1),dp[i-1][j-1]+(A[i]!=B[j]));
    cout<<dp[n][m]<<'\n';
    print(n,m);
    return 0;
}