T4 字符串的修改 题解

有 A=a1a2a3„am,B=b1b2b3„bn 两个字符串(均为小写字母)现在要通过以下操作将 A
或 A 的一个后缀修改为 B:
1. 删除 删除掉 A 中的某一个字符。
2. 添加 将某一个字符添加到 A 中任意位置。
3. 替换 将 A 中某一字符替换为另一个。
求出最小操作次数
第一行为字符串 A。第二行为字符串 B(长度均不超过 1000)。
题目描述很有让人爆搜的想法啊,看到数据,嗯,绝望了。
以这种大小明显不是爆搜可以解决的吧,看起来很像dp的样子呢,试试吧(重点我也没想到别的算法)
之前我看一个大佬的博客,大佬表示只要想通怎么去做就可以了,他每一步是怎么实现的不重要,仔细去想反而会更加迷糊。
(我上午试图仔细想想,试图把一些奇怪的问题都想出来,但我发现越来越乱,其实只要知道他让我们干什么,我们该怎么干就好了,具体会发生什么我们不需要知道。)
本来我是在写后缀的,dp[i][j]=a的后i个数要多少步才能变成b的前j个数。
但是这个代码有点太恶心人了,我脑子有点乱,感觉这不是什么好主意……
我就改了一下主意:dp[i][j]=a的前i个数要多少步才能变成b的前j个数。
大家想想啊,虽然这个是前缀,但如果前缀没什么用是不是可以忽略掉,竟然这样的话,代码清晰,思路也清晰就不是事了。
emm,写完了,但出了一点小问题。
大家都知道字符串第一个字符的下表是0,竟然是0的话,那么就不能出现什么dp[x-1]之类的东西,会崩掉的。
这不是很大的事情,但我处理的有些繁琐,导致自己出的一个数据一直过不去,还不知道为啥。
然后就来了一波操作,让输入的字符串下标整体向后移动了一下。
减少了奇怪的操作,问题解决了。
虽然我不知道为什么会有问题,但我知道是哪里出了问题,替代掉它就可以了。
一些奇怪的问题解决了,接下来是正经的dp了。
通过前面的说法,dp[i][j]表示a的前i个字符改变成b的前j个字符的次数。
我们可以得到一个简单好想的状态转化方程,就像新手礼包一样。dp[i][j]=min(dp[i-1][j-1]+bj,min(dp[i-1][j]+1,dp[i][j-1]));
emm,有点长,同学们可能看不懂,简单解释一下,我们把他分开,变成3部分
第一部分:dp[i-1][j-1]+bj
bj的意思是新加进来的两个字符是否相等,相等就抵消了嘛,不想等还要处理……
这一步还包含替换操作。
相等的话bj就是0,不相等就是1咯。
第二部分:dp[i-1][j]+1
第三部分:dp[i][j-1]+1
这2部分对应j-1和i-1的情况,我们会发现这两个有个共同点,就是现在的j比刚才的i多1个,所以我们只要在之前的里面再加上一个就可以解决了。
3个操作都想出来了,该放个代码了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<map>
#include<cstring>
using namespace std;
long long dp[1005][1005],ac,bc,bj;
char a[1005],b[1005];
int main()
{
	freopen("str.in","r",stdin);
	freopen("str.out","w",stdout);
	cin>>a+1>>b+1;
	ac=strlen(a+1);
	bc=strlen(b+1);
	for(int i=1;i<=ac;i++)//dp不初始化,考完两行泪啊。
	{
		dp[i][0]=0;
	}
	for(int i=1;i<=bc;i++)
	{
		dp[0][i]=i;
	}
	for(int i=1;i<=ac;i++)//简单和谐的代码
	{
		for(int j=1;j<=bc;j++)
		{
			if(a[i]==b[j])
			{
				bj=0;
			}else
			{
				bj=1;
			}
			dp[i][j]=min(dp[i-1][j-1]+bj,min(dp[i][j-1]+1,dp[i-1][j]+1));
		}
	}
	cout<<dp[ac][bc]<<endl;
	return 0; 	
}

思路写了这么多,代码只有40行(如果我不是这个码风可能还短),dp这种东西想起来有点难,写起来还是挺好写的。

附加:奉劝你们不要深究,明白为什么这么写就好,硬要想中间发生了什么会让脑子很乱(可能只有我这样),实在想不明白可以私下问我。看不看的见就随缘吧。

posted @ 2020-05-11 22:39  lichangjian  阅读(268)  评论(0编辑  收藏  举报