#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位对齐所需要的最少操作数量。初始状态有下面两种:

\[f[i][0] = 0 \\ f[0][i] = i \]

分别表示a的前i位和b的前0位(也就是什么都没有)对齐需要的最少操作数量,以及a的前0位和b的前i位对齐所需要的操作次数(也就是在a的前面加入i个数,操作次数是n)。

另外需要注意的就是,我们输入的是字符串a、b是从0到m-1,0到n-1存储的,输入之后需要把整个字符数组向后移一位。并且因为字符串长度过长,需要用char数组存储。

状态转移方程是这样的:

\[f[i][j] = min(f[i-1][j]+1,f[i][j-1]+1,f[i-1][j-1]+com(a[i],b[j])) \]

前两个的含义是:如果想把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;
} 
posted @ 2020-05-12 15:32  CYC的幸福生活  阅读(195)  评论(0编辑  收藏  举报