概念理解
对于A,B两字符串,定义三个基本操作分别为
- 插入一个字符
- 删除一个字符
- 修改一个字符
通过有限的进行上述三个基本操作,最终使A,B两个字符串完全一致,所进行的基本操作的次数成为两个字符之间的编辑距离
问题:
给出A,B两个字符串,计算两者之间的最小编辑距离
解法:
对于两个字符串
A—“aafojeaw”
B—“weafojfawe”
如果我们想要让两个字符串变得相同,那我们就需要从头开始一个一个对比两个字符串的字符,为了更清楚地记录这个过程,我们建立一张二维表
首先对于空字符串的编辑距离都是当前字符串的长度,因此直接填上
每个单元格内的数字表示横纵两坐标轴所对应的子串的编辑距离
字符串 | 0 | a | a | f | o | j | e | a | w |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
w | 1 | - | - | - | - | - | - | - | - |
e | 2 | - | - | - | - | - | - | - | - |
a | 3 | - | - | - | - | - | - | - | - |
f | 4 | - | - | - | - | - | - | - | - |
o | 5 | - | - | - | - | - | - | - | - |
j | 6 | - | - | - | - | - | - | - | - |
f | 7 | - | - | - | - | - | - | - | - |
a | 8 | - | - | - | - | - | - | - | - |
w | 9 | - | - | - | - | - | - | - | - |
e | 10 | - | - | - | - | - | - | - | - |
接下来我们一行一行一的看
(a,w)单元格对应的字符串"a"与"w"的编辑距离,很显然是1
字符串 | 0 | a | a | f | o | j | e | a | w |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
w | 1 | 1 | - | - | - | - | - | - | - |
e | 2 | - | - | - | - | - | - | - | - |
a | 3 | - | - | - | - | - | - | - | - |
f | 4 | - | - | - | - | - | - | - | - |
o | 5 | - | - | - | - | - | - | - | - |
j | 6 | - | - | - | - | - | - | - | - |
f | 7 | - | - | - | - | - | - | - | - |
a | 8 | - | - | - | - | - | - | - | - |
w | 9 | - | - | - | - | - | - | - | - |
e | 10 | - | - | - | - | - | - | - | - |
然后是(aa,w)单元格对应的是"aa"与"w"的编辑距离,很显然是2,但是这个地方我们可以认为,在(a,w)单元格已对齐的基础上,横轴增加一个字符意味着对横轴字符串执行了一个插入操作,所以其编辑距离=(a,w)+1
字符串 | 0 | a | a | f | o | j | e | a | w |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
w | 1 | 1 | 2 | - | - | - | - | - | - |
e | 2 | - | - | - | - | - | - | - | - |
a | 3 | - | - | - | - | - | - | - | - |
f | 4 | - | - | - | - | - | - | - | - |
o | 5 | - | - | - | - | - | - | - | - |
j | 6 | - | - | - | - | - | - | - | - |
f | 7 | - | - | - | - | - | - | - | - |
a | 8 | - | - | - | - | - | - | - | - |
w | 9 | - | - | - | - | - | - | - | - |
e | 10 | - | - | - | - | - | - | - | - |
以此类推我们可以填出这一整行的编辑距离,即在其左侧单元格所表示的已达到两字符串相同的编辑距离的基础上不断加1
字符串 | 0 | a | a | f | o | j | e | a | w |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
w | 1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
e | 2 | - | - | - | - | - | - | - | - |
a | 3 | - | - | - | - | - | - | - | - |
f | 4 | - | - | - | - | - | - | - | - |
o | 5 | - | - | - | - | - | - | - | - |
j | 6 | - | - | - | - | - | - | - | - |
f | 7 | - | - | - | - | - | - | - | - |
a | 8 | - | - | - | - | - | - | - | - |
w | 9 | - | - | - | - | - | - | - | - |
e | 10 | - | - | - | - | - | - | - | - |
然后我们来看第二行,单元格(a,we)对应的编辑距离是2,这里可以简单理解为在纵轴上不断增加一个字符,因此就是在上一单元格的编辑距离的基础上不断加1
字符串 | 0 | a | a | f | o | j | e | a | w |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
w | 1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
e | 2 | 2 | - | - | - | - | - | - | - |
a | 3 | 3 | - | - | - | - | - | - | - |
f | 4 | 4 | - | - | - | - | - | - | - |
o | 5 | 5 | - | - | - | - | - | - | - |
j | 6 | 6 | - | - | - | - | - | - | - |
f | 7 | 7 | - | - | - | - | - | - | - |
a | 8 | 8 | - | - | - | - | - | - | - |
w | 9 | 9 | - | - | - | - | - | - | - |
e | 10 | 10 | - | - | - | - | - | - | - |
如果我们专注于某一轴的字符串,比如说专注于横轴字符串"aafojeaw",当我们在求横向解时,可以看作是为了让横轴匹配纵轴而不断进行删除字符操作,而求解纵向解时,则可以看作是为了让横轴匹配纵轴而不断进行插入字符操作。反之专注于纵轴字符串同理
因此对于某一单元格,当我们想要从之前的已求解过子串去逼近他时,可以选择从上方逼近,也可选择从左侧逼近,两者对应的是删除和插入操作(专注于横轴时,上方为插入,左侧为删除,专注于纵轴时,上方删除,左侧为插入)
接下来我们看一下(aa,we)单元格,很显然我们发现这个的单元格的编辑距离是2,但如果我们采用前面所找到的规律会发现无论是从左侧还是从上方逼近,都无法得到2,因为这里我们既没有采用插入,也没有采用删除,而是使用修改,对于修改操作,我们让(a,w)变为一致,然后在(a,w)一致的基础上,我们再次修改使(aa,we)变为一致,因此仅仅需要两步操作,从这个过程中我们发现这一步直接从(a,w)的一致跳到了(aa,we)的一致,这说明修改操作是沿着左上到右下的对角线进行的,每当我们要进行修改操作时,只要在左上角的基础上,选择+1或+0(分别对应需要修改和不需要修改)即可
由此我们已经使用了所有的基本操作,找到了填这个二维表的完整方法,按照这个原则我们将表填完
字符串 | 0 | a | a | f | o | j | e | a | w |
---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
w | 1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
e | 2 | 2 | 2 | 3 | 4 | 5 | 5 | 6 | 7 |
a | 3 | 3 | 2 | 3 | 4 | 5 | 6 | 5 | 6 |
f | 4 | 4 | 3 | 2 | 3 | 4 | 5 | 6 | 6 |
o | 5 | 5 | 4 | 3 | 2 | 3 | 4 | 5 | 6 |
j | 6 | 6 | 5 | 4 | 3 | 2 | 3 | 4 | 5 |
f | 7 | 7 | 6 | 5 | 5 | 4 | 3 | 4 | 5 |
a | 8 | 8 | 7 | 6 | 6 | 5 | 4 | 3 | 4 |
w | 9 | 9 | 8 | 7 | 7 | 6 | 5 | 4 | 4 |
e | 10 | 10 | 9 | 8 | 8 | 7 | 6 | 5 | 5 |
由此我们得到横纵两轴所对应的完整字符串的编辑距离便是右下角的编辑距离,即5
在我们求解的过程中,每一步我们都选取了之前的选择中最优的步骤,因此直到最后我们所得到的的步骤便是全局最优解、
这看起来和贪心算法一样,但其实有着一定的差别
贪心算法选取当前步骤的最优解后,这个解便确定下来,后续操作不会回头检查这个解是否真的是全局最优,因此贪心算法只能达到局部最优解。
但是这里的算法,我们在填表的时候会发现某些已经确定的解法会由于之前存在更好的解而被抛弃,尤其是当我们发现编辑距离在不断减小的时候,这是因为之前的最优解被保存了下来,这个表相当于穷举了两个字符串每个位置可能的操作,然后根据已经得到的结果选取一个最优解并保存下来,因此我们不断向后求解时,会反复的使用到之前确定的可能的最优解,进而选取一条真正的最优步骤
这就是动态规划的思想,这道题目也是动态规划的一道经典例题