#luogu整理 T133316 字符串修改
初三返校测试T4:字符串的修改(DP)
luogu T133316 字符串的修改
题目描述:
有 A=a1a2a3……am,B=b1b2b3……bn 两个字符串(均为小写字母)现在要通过以下操作将 A 或 A 的一个后缀修改为 B: 1. 删除 删除掉 A 中的某一个字符。 2. 添加 将某一个字符添加到 A 中任意位置。 3. 替换 将 A 中某一字符替换为另一个。 求出最小操作次数。
输入格式
第一行为字符串 A。第二行为字符串 B(长度均不超过 1000)。
输出格式
一个正整数,最小操作次数。
输入样例
aaab
aabc
输出样例
1
思路
动态规划,f[i][j]表示a的前i位和b的前j位对齐,需要的最少操作次数。最终需要得到的答案就是f[m][n],即的前m位和b的前n位对齐所需要的最少操作数量。初始状态有下面两种:
分别表示a的前i位和b的前0位(也就是什么都没有)对齐需要的最少操作数量,以及a的前0位和b的前i位对齐所需要的操作次数(也就是在a的前面加入i个数,操作次数是n)。
另外需要注意的就是,我们输入的是字符串a、b是从0到m-1,0到n-1存储的,输入之后需要把整个字符数组向后移一位。并且因为字符串长度过长,需要用char数组存储。
状态转移方程是这样的:
前两个的含义是:如果想把a的前i位和b的前j位对齐,我们可以找a的前i-1位已经和b的前j位对齐的操作次数,再用一次操作把这个字符串“平移”一下得到。怎么平移?根据题目给的条件,如果a的前i-1位已经和b的前j位对齐,那我在a的最前面添加一个数字,第i-1位就变成第i位了。这种操作在逻辑上讲和平移是一样的。
第三个的含义是:如果想把a的前i位和b的前j位对齐,我们可以找a的前i-1位已经和b的前j-1位对齐的操作次数,再判断一下a[i]和b[j]是否相等,如果相等,就不需要操作;如果不相等,就操作数+1。(com函数就是用来判断这个的。)
代码
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
int f[1009][1009];
char a[1099],b[1099];//string字符串会爆,所以必须用char数组
int strlen(char x[]){//手写字符串长度函数
int tot = 0;
while(x[tot++] != '\0');
tot--;
return tot;
}
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];
}
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;
}