题目描述:
要求两字符串有差异的字符个数。例如:
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) {
}
void editDistance(int len1,int len2) {
}
int main() {
}
参考:
http://liutos.github.io/posts/算法/计算两字符串的编辑距离.html
http://blog.csdn.net/yysdsyl/article/details/4249245