题目描述
有 A=a1a2a3……am,B=b1b2b3……bn 两个字符串(均为小写字母)现在要通过以下操作将 A 或 A 的一个后缀修改为 B: 1. 删除 删除掉 A 中的某一个字符。 2. 添加 将某一个字符添加到 A 中任意位置。 3. 替换 将 A 中某一字符替换为另一个。 求出最小操作次数。
输入格式
第一行为字符串 A。第二行为字符串 B(长度均不超过 1000)。
输出格式
一个正整数,最小操作次数。
输入输出样例
aaab aabc
1
说明/提示
1 次操作 使用后缀 aab 在末尾插入 c
思路:其实这是一道dp题,那么首先要找状态转移方程,那么开一个二维数组f(其实开几维除了刷题多有感觉,也可以看取值范围来判断。比如说这个题的取值范围是1000以内,那么我开二维数组复杂度是不会炸的。如果范围在100以内,那么我就可以考虑开三维数组。回到这个二维数组,这个数组f[i][j],分别指的是字符串a的前i位和字符串b的前j位对齐需要几步。来看状态转移方程:
f[i][j]=min(f[i-1][j]+1,min(f[i][j-1]+1,f[i-1][j-1]+pd(i,j)));
下面来解释一下这个方程:首先这个方程其实就是比较三个数谁比较小。那么先看第一个,a的前i-1位和b的j位需要的步数+1,这其实是因为如果a的前i-1位能和b的前j位对齐,那么只需要把前i-1位往后平移一下就好了,所以相当于在a的前面添加一个,这相当于是1步。再看第二个,如果a的前i位能和b的i-1位对齐,那么b现在多增加了一位,如果a也多增加一位那不就行了吗,所以步数+1也可以完成。最后一个,如果a的i-1位和b的i-1位能对齐,那么其实只需要看a的第i位和b的第j位就可以了。如果不一样,那么换掉其中一个即可,就是一步。如果脸白,这两个数一样,那么就不需要加步数了,即+0,只需要写一个函数判断即可,如果相同返回1,不同返回0。那么整个代码就很好写了。
但是还有一个问题,初始化该怎么办?我们想,如果a是前0位,那么不管b是什么字符串,肯定是要添加一个b字符串,才能对齐,所以f[0][i]=i。同理,如果b是前0位,那么字符串a根本不用动也可以跟0位对齐,因为0位相当于没有啊,所以f[i][0]=0。开始写代码!
代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
int f[1009][1009];
char a[1099],b[1099];//string字符串会爆,所以必须用char数组
int com(int x,int y){//如果相同返回0,不同就返回1
if(a[x] == b[y]) return 0;
return 1;
}//判断函数
int main(){
cin >> a >> b;
int m = strlen(a),n = strlen(b);
for(int i = max(m,n);i >= 1; i--){
a[i] = a[i-1];
b[i] = b[i-1];
}//这里也是一步细节操作,因为前一位应该是1个字母。但是字符串输入是从0开始数,所以要后移一位
for(int i = 0;i <= m; i++){
f[i][0] = 0;//a的前i位和b的前0位对齐
}
for(int i = 0;i <= n; i++){
f[0][i] = i;//a的前0位和b的前i位对齐
}
for(int i = 1;i <= m; i++) {
for(int j = 1;j <= n; j++){
f[i][j] = min(f[i-1][j] + 1,min(f[i][j-1] + 1, f[i-1][j-1] + com(i,j)));//状态转移
}
}
cout << f[m][n] << endl;
return 0;
}