编辑距离
A 题面
设A和B是2个字符串。要用最少的字符操作将字符串A转换为字符串B。这里所说的字符操作包括:
(1)删除一个字符;
(2)插入一个字符;
(3)将一个字符改为另一个字符。
求将字符串A变换为字符串B所用的最少字符操作数称为字符串A到B的编辑距离,记为d(A,B)。
B 分析
我将做dp题目大致划分为四步:
- 确定子问题
- 根据子问题定义状态
- 得到状态转移方程
- 思考边界问题
1.确定子问题:
对于一个字符,我们有三种操作:增 删 改
(其实不操作也是一种操作)
所以本题的子问题就是:
在进行了这三种操作过后
现在的a串到b串用了多少步
2.根据子问题定义状态
因为子问题是求让 a 成为 b 的步数
所以我们定义f[i][j]表示 将串a[1…i]转换为串b[1…j]所需的最少操作次数
所以我们的答案就在f[lenA][lenB]
(注意a 和 A 不是同一个字符串,b 和 B 也不是)
(A是原串,B是原目标串)
(a是操作过后的原串,b是操作过后的B)
3.得到状态转移方程
(1)增:可以看做与B串最后一个字符抵消后不再考虑这个字符
所以f(i,j)=min(f(i,j),f(i,j-1)+1);
(2)删:可以看做把A串最后一个字符删去后不再考虑这个字符
所以f(i,j)=min(f(i,j),f(i-1,j)+1);
(3)改:可以看作抵消了A、B串最后的两个字符
所以f(i,j)=min(f(i,j),f(i-1,j-1)+1);
当然这种情况有特例,就是当A,B串这两个字符相等的时候
此时:f(i,j)=f(i-1,j-1);
for(int i=1;i<=lena;i++){ for(int j=1;j<=lenb;j++){ if(a[i-1]==b[j-1]){ f[i][j]=f[i-1][j-1]; continue; } f[i][j]=min(min(f[i-1][j],f[i][j-1]),f[i-1][j-1])+1; } }
需要注意的是:在执行增删改操作后不要忘了+1
4.思考边界问题
(1)i==0时,即a串为空,那么对应的f[0][j]的值就为j:
这时的处理方法是:增加j个字符,使a转化为b
(2)j==0时,即b为空,那么对应的f[i][0]的值就为i:
这时的处理方法是:减少i个字符,使a转化为b
for(int i=0;i<=lena;i++)f[i][0]=i;
for(int i=0;i<=lenb;i++)f[0][i]=i;
5.答案
显然答案的位置就在f[lena][lenb];
C 代码
#include <bits/stdc++.h> using namespace std; int f[2005][2005]; char a[2005],b[2005]; int main(){ int alen,blen; cin>>a>>b; alen=strlen(a); blen=strlen(b); for(int i=0;i<=alen;i++){ f[i][0]=i; } for(int i=0;i<=blen;i++){ f[0][i]=i; } for(int i=1;i<=alen;i++){ for(int j=1;j<=blen;j++){ if(a[i-1]==b[j-1]){ f[i][j]=f[i-1][j-1]; } else{ f[i][j]=min(min(f[i-1][j],f[i][j-1]),f[i-1][j-1])+1; } } } cout<<f[alen][blen]; return 0; }
D 总结
在这里小小地总结一下如何解决一般dp问题:
1.确定子问题
2.根据子问题定义状态
3.得到状态转移方程
4.思考边界问题
5.写code
E 扩充
上面是解释记忆化搜索和dp关系的一张图
所以这道题其实可以用递归的方法写出来
#include<bits/stdc++.h> using namespace std; char a[2005],b[2005]; int f[2005][2005]; int dp(int i,int j){ if(f[i][j]!=-1)return f[i][j];//记忆化搜索 if(i==0) return f[i][j]=j;//边界 if(j==0) return f[i][j]=i;//边界 int bonus=1; //是否执行了操作 if(a[i]==b[j])bonus=0; //没有执行操作 return f[i][j]=min(min(dp(i-1,j)+1,dp(i,j-1)+1),dp(i-1,j-1)+bonus); } int main(){ cin>>a>>b; memset(f,-1,sizeof(f)); int lena=strlen(a); int lenb=strlen(b); dp(lena,lenb); cout<<f[lena][lenb]; return 0; }