给定两个字符串如何判断它们的相似程度?常用的有两种方法:
- 编辑距离(edit distance)
- 最长公共子序列(LCS,longest common sequence)
解释两个概念:
- 编辑距离,指的是两个字符串之间,由一个转换成另一个所需的最少编辑操作次数。许可的编辑操作包括:(1)将一个字符替换成另一个字符,(2)插入一个字符,(3)删除一个字符。
- 相似度,等于“编辑距离+1”的倒数。
一开始,我看到编辑距离的代码时,差点以为就是最长公共子序列。后来才发现两者不太一样。
下面就递推公式进行解释,f[i][j]表示a[0:i]和b[0:j]两个串进行比较所得的结果。
最长公共子序列
- a[i]==b[j]时,f[i][j]=f[i-1][j-1]+1
- a[i]!=b[j]时,f[i][j]=max(f[i-1][j],f[i][j-1])
编辑距离
- a[i]b[j]时,f[i][j]=f[i-1][j-1]
意思是,当a[i]b[j]时,无需进行编辑操作 - a[i]!=b[j]时,需要进行编辑操作,这是编辑可以从三种操作中进行选择:插入、删除、修改
细分之,又可得到6种操作:
在a[i]前面插入b[j],f[i][j]=f[i-1][j]+1
在b[j]前面插入a[i],f[i][j]=f[i][j-1]+1
删除a[i],f[i][j]=f[i-1][j]+1
删除b[j],f[i][j]=f[i][j-1]+1
修改a[i],使之等于b[j],f[i][j]=f[i-1][j-1]+1
修改b[j],使之等于a[i],f[i][j]=f[i-1][j-1]+1
显然,操作虽然是6种,但是结果却只有三种。
如果选择插入操作,在a[i]前面插入一个字符b[j]。那么a[0:i-1]和b[0:j]字符都相同需要的编辑次数为f[i-1][j],所以f[i][j]=f[i-1][j]+1
如果选择删除操作,把a[i]这个字符删除,这样一来b[j]就不用跟a[i]进行比较了。那么a[0:i]和b[0:j-1]字符都相同,需要的编辑次数是f[i][j-1],所以f[i][j]=f[i][j-1]+1
如果选择修改操作,把a[i]改成b[j],那么a[0:i-1]和b[0:j-1]部分比较次数为f[i-1][j-1],所以f[i][j]=f[i-1][j-1]+1
因为需要编辑次数尽量少,所以f[i][j]会从以上三种可行操作中选择一种花费最少的。
也就是说,f[i][j]=min(f[i-1][j],f[i][j-1],f[i-1][j-1])+1
显然,把上述在a中插入改为在b中插入、在a中删除改为在b中删除,所得到的递推式是一样的。