一、贝叶斯定理
编辑距离是指两个字符串之间,由一个转换成另一个所需的最少编辑操作次数。编辑操作包括删除、添加(插入)和替换三种方式。当然,次数越小越相似。
编辑距离应用最多的地方就是基于贝叶斯定理的拼写检查中,贝叶斯公式:
用户输入一个单词时,可能拼写正确,也可能拼写错误。如果把拼写正确的情况记做c(代表correct),拼写错误的情况记做w(代表wrong),那么"拼写检查"要做的事情就是:在发生w的情况下,试图推断出c。换言之:已知w,然后在若干个备选方案中,找出可能性最大的那个c,也就是求的最大值。
而根据贝叶斯定理,有:
由于对于所有备选的c来说,对应的都是同一个w,所以它们的P(w)是相同的,因此我们只要最大化
即可。其中:
- P(w|c)表示在试图拼写c的情况下,出现拼写错误w的概率。为了简化问题,假定两个单词在字形上越接近,就有越可能拼错,P(w|c)就越大。举例来说,相差一个字母的拼法,就比相差两个字母的拼法,发生概率更高。你想拼写单词genius,那么错误拼成genious(相差一个字母)的可能性,就比拼成genioous高(相差两个字母),而这种问题就是“编辑距离”问题。
- P(c)表示某个正确的词的出现"概率",它可以用"频率"代替。如果我们有一个足够大的文本库,那么这个文本库中每个单词的出现频率,就相当于它的发生概率。某个词的出现频率越高,P(c)就越大。比如在你输入一个错误的词“genious”时,系统更倾向于去猜测你可能想输入的词是“genius”,而不是“genieus”,因为“genius”更常见。
二、字符串编辑距离
举个例子:S=“eeba” ,T="abac" 。我们发现当S只有一个字符e、T只有一个字符a的时候,我们马上就能得到S和T的编辑距离edit(0,0)=1(将e替换成a)。那么如果S中有1个字符e、T中有两个字符ab的时候,我们是不是可以这样分解:edit(0,1)=edit(0,0)+1(将e替换成a后,在添加一个b)。如果S中有两个字符ee,T中有两个字符ab的时候,我们是不是可以分解成:edit(1,1)=min(edit(0,1)+1, edit(1,0)+1, edit(0,0)+f(1,1))。 这样我们可以得到这样一些动态规划公式:
如果i=0且j=0 edit(0, 0)=1
如果i=0且j>0 edit(0, j )=edit(0, j-1)+1
如果i>0且j=0 edit( i, 0 )=edit(i-1, 0)+1 //以上三行是初始化二维数组时的操作
如果i>0且j>0 edit(i, j)=min(edit(i-1, j)+1, edit(i,j-1)+1, edit(i-1,j-1)+f(i , j) ) //f(i , j) =0 or 1
注:
1) edit(i,j)表示S中[0.... i]的子串 si 到T中[0....j]的子串t1的编辑距离。f(i,j)表示S中第i个字符s(i)转换到T中第j个字符s(j)所需要的操作次数,如果s(i)==s(j),则不需要任何操作f(i, j)=0; 否则,需要替换操作,f(i, j)=1 。
2) edit(i-1, j)+1:删除操作;
edit(i,j-1)+1: 添加操作;
edit(i-1,j-1)+f(i , j):替换操作,等价于edit(i-1,j-1)+(s[i] = t[j] ? 0: 1)
递推公式可以这样理解:
我们对字符可能进行的操作有三种:
如果我们可以使用k个操作数把s[1…i]转换为t[1…j-1],我们只需要把t[j]加在最后面就能将s[1…i]转换为t[1…j],操作数为k+1;
如果我们可以使用k个操作数把s[1…i-1]转换为t[1…j],我们只需要把s[i]从最后删除就可以完成转换,操作数为k+1;
如果我们可以使用k个操作数把s[1…i-1]转换为t[1…j-1],我们只需要在需要的情况下(s[i] != t[j])把s[i]替换为t[j],所需的操作数为k+cost(cost
代表是否需要转换,如果s[i]==t[j],则cost为0,否则为1)。
将s[1…n]转换为t[1…m]当然需要将所有的s转换为所有的t,所以,d[n,m](表格的右下角)就是我们所需的结果。
另外,必须记住一点:这所有的操作都是针对i,即源字符串的。如,删除的是源字符串i上的字符...
代码如下:
int getDistance(char[] str1,char[] str2)
{
int n = str1.length;
int m = str2.length;
int[][] C = new int[n+1][m+1];
int i, j, x, y, z;
for (i = 0; i <n; i++)
C[i][0] = i;
for (i = 0; i < m; i++)
C[0] [i] = i;
for (i = 1; i < n+1; i++)
{
for (j = 1; j < m+1; j++)
{
x = c[i][j-1] + 1;
y = c[i-1][j] + 1;
if (str1[i] == str2[j])
z = c[i-1][j-1];
else
z = c[i-1][j-1] + 1;
c[i][j] = min(min(x, y), z);
}
}
return c[n][m];
}