题目描述
有二个字串 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;
}
}