两字符串的编辑距离 (Eidt Distance)

题目描述:

要求两字符串有差异的字符个数。例如: 
aaaaa b aaaaa 
aaaaa c aabaa 
这两个字符串,最大公共字串长度是5,但它们只有两个字符不同,函数输出值应为2。 
如果是: 
aaa bbbccc ddd 
aaa eee ddd 
函数的输出值应该是6。 

比较形象地形容一下,把两个字符串排成上下两行,每个字符串都可以在任何位置插入空格以便上下对齐,每个列上至少有一个字符来自这两个字符串。当对齐程度最高的时候,没有对上的列的数即为函数输出值。 
aaa bbbccc ddd 
aaa eee ddd 
最优对齐状态是: 
aaa bbbccc ddd 
aaa eee    ddd 
没有对上的列是6,函数输出值为6。 
如果是: 
abcde 
acefg 
最优对齐状态是: 
abcde 
a  c  efg 
没有对上的列数是4,函数输出值为4。

 

 

问题抽象归类:(编辑距离问题)

设A和B是2个字符串。要用最少的字符操作将字符串A转换为字符串B。这里所说的字符操作包括:

(1)删除一个字符;
(2)插入一个字符;
(3)将一个字符改为另一个字符。
将字符串A变换为字符串B所用的最少字符操作数称为字符串A到B的编辑距离,记为d(A,B)。试设计一个有效算法,对任给的2个字符串A和B,计算出它们的编辑距离d(A,B)。
要求:
输入:第1行是字符串A,第2行是字符串B。
输出:字符串A和B的编辑距离d(A,B)



什么是编辑距离?

《算法概论》第六章动态规划的内容安排中,继第二节寻找最长子序列的问题后,就是计算两个字符串之间的编辑距离的算法了。那么所谓的编辑距离是怎么一回事呢?举个例子,我在写一篇英文文章,然后发现自己有一个单词sunny写错了,写成了snowy。(好吧这错得有点离谱了)现在我要把这个单词改正过来,那么可以怎么改呢?事实上改的方法很多,这里直接用《算法概论》中给出的两种修改方式好了,第一种是

s-nowy sunn-y
这是什么意思呢?可以这样理解:当上一行的字符为连字符-时,意思是“如果要把上面的字符串尽量修改成下面的字符串的样子,那么就要这里插入下面的字符”。例如连字符对应的字符是u,那么意思就是要在suowy这个单词的s和u之间插入字符u以一步步地向sunny这个单词变化;当上下两行的字符不一致时,意思就是上面的字符串如果想要变成马猴烧酒……啊,不,是变成下面的字符串,那么就要把这个位置的字符替换成下面的那一个;至于上面一行是正常字符而下面一行是连字符的情况,则表示要把上面一行的那个字符删除掉——其实可以反过来理解,如果上下行对调,那么在那个位置就相应地变成插入,所以反过来就是删除。第二中变换方式是

-snow-y sun--ny
比较上面的两种变换方式,显然,第一种需要做的修改比较少,是3次,而第二种需要做的修改则是5次,并且再也找不到比3次修改更小的次数了。这个最少的修改次数,就是两个字符串之间的编辑距离。显然,交换两个字符串的变换方向,编辑距离是不会变的。

 

 

思路:动态规划

开一个二维数组d[i][j]来记录a0-ai与b0-bj之间的编辑距离,要递推时,需要考虑对其中一个字符串的删除操作、插入操作和替换操作分别花费的开销,从中找出一个最小的开销即为所求

具体算法:

首先给定第一行和第一列,然后,每个值d[i,j]这样计算:

d[i][j]   =   min(d[i-1][j]+1,d[i][j-1]+1,d[i-1][j-1]+(s1[i]  ==  s2[j]?0:1));   

 最后一行,最后一列的那个值就是最小编辑距离 

 一次我完全没有自己去想过子问题是什么,所以只好直接给出《算法概论》中的答案了。书中所定义的子问题是“这两个字符串的前缀之间的编辑距离”。事实上,除了定义子问题,我们应该首先思考的一个问题是怎么定义最优子结构?首先我们需要名为“修改”的概念。将一个字符串变换成另一个字符串的插入、删除和修改的过程的总和,叫做修改。修改的次数就可以叫做这个修改方式的代价,而其中修改次数对应于编辑距离的,也就是修改次数最少的修改方式,就叫做所谓的最佳修改好了(这名字是我瞎起的=。=)。现在,我们可以借助所定义的子问题和修改的概念,把最优子结构定义为“两个字符串之间的最佳修改,一定包含了它们的各自前缀之间的最佳修改”。

现在可以直接定义最优解的值了,也就是编辑距离的计算方法。对于两个字符串,如果要将其中一个修改为另外一个,那么只有三种可能:一、把要变换的字符串的最后一个字符删掉;二、把目标字符串的最后一个字符插入到要被变的字符串的最后;三、把两个字符串的右边对齐了,然后看看最后一个字符串是不是一样的。如果是那就不需要任何改变,否则就把原来的字符替换掉。不管是删掉、插入还是替换,修改次数都是一,剩下的一种情况修改次数为零。假设计算编辑距离的函数叫做E(i, j)(其中i和j是两个字符串分别取出的前缀长度),那么它的计算公式就是

E(m, n)=min{1+E(i-1, j), 1+E(i, j-1), diff(i, j)+E(i-1, j-1)}。
其中函数diff就是用于在两个字符串最右对齐的情况下,计算出修改次数的。E函数的基本情况是i为零、j为零或者i和j同时为零,它们的计算结果分别是j、i和0。

然后就可以开始自底向上地进行计算了。这一次,计算最优解的值的函数E(i, j)是一个二元函数,因此为了保存中间计算结果,需要维护一个二维矩阵。填表的过程很简单,只要从上往下、一行一行地按照上面给出的公式计算就可以了。编辑距离很容易计算,就是在矩阵的右下角的元素的值而已。相反,我觉得有难度的地方在于从所得到的矩阵中得出如何进行变换的信息,这个步骤倒是想了我颇久的,还好也解决了。

 

代码:

#include    
#include    
char s1[1000],s2[1000];   
int min(int a,int b,int c) {   
    int t = a < b ? a : b;   
    return t < c ? t : c;   
}   
void editDistance(int len1,int len2) {   
    int** d=new int*[len1+1];
    for(int k=0;k<=len1;k++)
        d[k]=new int[len2+1];  
    int i,j;   
    for(i = 0;i <= len1;i++)   
        d[i][0] = i;   
    for(j = 0;j <= len2;j++)   
        d[0][j] = j;   
    for(i = 1;i <= len1;i++)   
        for(j = 1;j <= len2;j++) {   
            int cost = s1[i] == s2[j] ? 0 : 1;   
            int deletion = d[i-1][j] + 1;   
            int insertion = d[i][j-1] + 1;   
            int substitution = d[i-1][j-1] + cost;   
            d[i][j] = min(deletion,insertion,substitution);   
        }   
    printf("%d/n",d[len1][len2]); 
    for(int k=0;i<=len1;k++)
        delete[] d[k];
    delete[] d;
}   
int main() {   
    while(scanf("%s %s",s1,s2) != EOF)   
        editDistance(strlen(s1),strlen(s2));   
}  
参考:
http://liutos.github.io/posts/算法/计算两字符串的编辑距离.html
http://blog.csdn.net/yysdsyl/article/details/4249245

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值