Edit(动态规划)(编辑距离问题)

题目描述

有二个字串 str1, str2 , 字串str2 可以经过若于次变换后变成str1,其变换的规则是:

  1) 可以在任何位置加入字符;

  2) 可以删除任何字符;

  3) 可以改变任何字符。

  约定:增加、删除与改变任何一个字符称为一次变换。

  例如:str1=‘bcadef ’ str2=’abcedkk’

  可以经过的变换为:

  1)删去第一个字符‘a’ str2=’bcedkk’

  2)改变一个字符,将‘e’变成‘a’ str2=’bcadkk’

  3)改变一个字符,将‘k’变成‘e’ str2=’bcadek’

  4) 改变一个字符,将‘k’变成‘f’ str2=’bcadef’

  此时,str2 经过4次变换后变成str1。

  问题:给出str1,str2,要求用最少的变换次数从str2变换为str1 。

输入样例

bcadef
abcedkk

输出样例

4

思路

一开始以为是一个最长公共子序列的问题,乍一看很像回文词的类似题目,因为最长公共子序列就是两个字符串最多相似的有几个元素,又因为增删改都算作是一次操作,所以如果求出他们最长的公共子序列,然后取两个字符串中的比较长的那个字符串长度减去最长公共子序列的长度,得出的不就是题目所要求的最少操作次数吗?

于是我按照这个思路写了代码:

求出两个字符串的最长公共子序列,然后取长的长度减去最长公共子序列

代码如下:

#include<bits/stdc++.h>
using namespace std;
char s1[1000],s2[1000];
int dp[500][500],l1,l2,max1;
int main()
{
	while(cin>>s1>>s2)
	{
		memset(dp,0,sizeof(dp));
		l1=strlen(s1);
		l2=strlen(s2);
		for(int i=1;i<=l1;i++)
		{
			for(int j=1;j<=l2;j++)
			{
				if(s1[i-1]==s2[j-1])
					dp[i][j]=dp[i-1][j-1]+1;
				else
					dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
			}
		}
		
		cout<<max(l1,l2)-dp[l1][l2]<<endl;
	}
}

提交之后显示对了50%,数据对比如下:

 对比之后百思不得其解,怎么会比答案的最小编辑距离还小呢?后来才想明白,没有考虑到顺序问题,反例如下:

abc

bxx

很明显,最长公共子序列是1,但是顺序问题导致了最短的编辑距离就是3,所以不能用最长公共子序列完成,所以要分开讨论这几种操作:

如果当前比对的两个元素相同,则不需要进行任何操作,所以状态转移方程就为

dp[i][j]=dp[i-1][j-1]
//当前的两个字母相同,不需要任何操作
//dp[i][j]的最小操作数量即为两个字符串去掉当前的字符---->dp[i-1][j-1]

如果不相同,我们则有三种操作方式,插入,删除,修改

如果是插入:

如果在i这个数组插入,是和j的当前元素抵消了,所以去掉j当前元素

dp[i][j-1]+1

如果在j这个数组插入,是和i的当前元素抵消了,所以i当前元素 

dp[i-1][j]+1

如果是删除:

同理可得是和插入一样的

dp[i][j-1]+1
dp[i-1][j]+1

如果是修改

那么不论是修改哪个字符串的,当前的两个元素都会抵消,所以最小编辑距离就是都去掉一个元素之后的最小编辑距离加1

dp[i-1][j-1]+1

最后几种操作比较一下,取最小值就可以

同时给一下初始值

当某一个字符串为空的时候,另一个字符串有长度,那么最短编辑距离就为另一个字符串的长度

例:

第一个字符串:(空)

第二个字符串:abc

那么此时最短编辑距离就为3,其实就是进行三次插入操作

for(int i=1;i<=l1;i++)dp[i][0]=i;
for(int i=1;i<=l2;i++)dp[0][i]=i;

最终代码

最后代码如下:

#include<bits/stdc++.h>
using namespace std;
char s1[1000],s2[1000];
int dp[500][500],l1,l2,max1;
int minn(int a,int b,int c)
{
	int minx=min(a,b);
	minx=min(minx,c);
	return minx;
}
int main()
{
	while(cin>>s1)
	{
		memset(dp,0,sizeof(dp));
		l1=strlen(s1);
		l2=strlen(s2);
		for(int i=1;i<=l1;i++)dp[i][0]=i;
		for(int i=1;i<=l2;i++)dp[0][i]=i;
		for(int i=1;i<=l1;i++)
		{
			for(int j=1;j<=l2;j++)
			{
				if(s1[i-1]==s2[j-1]){dp[i][j]=dp[i-1][j-1];}
				else{dp[i][j]=dp[i-1][j-1]+1;}	

				dp[i][j]=minn(dp[i-1][j]+1,dp[i][j-1]+1,dp[i][j]);
			}
		}

		cout<<dp[l1][l2]<<endl;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水轻侮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值