CF56D Changing a String 题解
双倍经验:P2758。
令 \(dp_{i,j}\) 表示 \(s\) 前 \(i\) 个字符要变成 \(t\) 前 \(j\) 个字符所需的最少移动次数。
答案即为 \(dp_{\lvert s \rvert,\lvert t \rvert}\)。
显然有初始状态 \(dp_{i,0}=dp_{0,i}=i\)。
因为我们只可能从添、删、替三种操作转移而来,
于是有转移方程:
- 添:
- 删:
- 替:
三者取 \(\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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】