编辑距离算法求文本相似度

1 概念

编辑距离,又称Levenshtein距离(莱文斯坦距离也叫做Edit Distance),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数,如果它们的距离越大,说明它们越是不同。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。

在概念中,我们可以看出一些重点那就是,编辑操作只有三种。插入,删除,替换这三种操作,我们有两个字符串,将其中一个字符串经过上面的这三种操作之后,得到两个完全相同的字符串付出的代价是什么就是我们要讨论和计算的。
例如:

如果str1=”ivan”,str2=”ivan”,那么经过计算后等于 0。没有经过转换。相似度=1-0/Math.Max(str1.length,str2.length)=1
如果str1=”ivan1”,str2=”ivan2”,那么经过计算后等于1。str1的”1”转换”2”,转换了一个字符,所以距离是1,相似度=1-1/Math.Max(str1.length,str2.length)=0.8

计算相似度公式:1-它们的距离/两个字符串长度的最大值。

2 算法过程

2.1 算法解析

首先定义这样一个函数——edit(i, j),它表示第一个字符串的长度为i的子串到第二个字符串的长度为j的子串的编辑距离。
显然可以有如下动态规划公式:

if i == 0 且 j == 0,edit(i, j) = 0
if i == 0 且 j > 0,edit(i, j) = j
if i > 0 且j == 0,edit(i, j) = i
if i ≥ 1 且 j ≥ 1 ,edit(i, j) == min{ edit(i-1, j) + 1, edit(i, j-1) + 1, edit(i-1, j-1) + f(i, j) },当第一个字符串的第i个字符不等于第二个字符串的第j个字符时,f(i, j) = 1;否则,f(i, j) = 0。

例如计算:
ofailing和osailn这两个字符串的相似度。根据上面公式。

 
image.png

 

计算edit(1, 1),edit(0, 1) + 1 == 2,edit(1, 0) + 1 == 2,edit(0, 0) + f(1, 1) == 0 + 1 == 1,min(edit(0, 1),edit(1, 0),edit(0, 0) + f(1, 1))==1,因此edit(1, 1) == 1。 依次类推:

 

 
image.png

 

edit(2, 1) + 1 == 3,edit(1, 2) + 1 == 3,edit(1, 1) + f(2, 2) == 1 + 0 == 1,其中s1[2] == 'a' 而 s2[1] == 'f'‘,两者不相同,所以交换相邻字符的操作不计入比较最小数中计算。以此计算,得出最后矩阵为:

 

 
image.png


状态转移,从edit(i, j) 三个方向转来
edit(i-1, j) + 1, edit(i, j-1) + 1 表示插入或删除一个字符
edit(i-1, j-1) + f(i, j) f(i,j)=0 表示不替换 f(i,j)=1表示替换1次

2.2 实现代码(Java版)

public int minDistance(String word1, String word2) {
        if (word1.length() == 0 || word2.length() == 0)
            return word1.length() == 0 ? word2.length() : word1.length();
        int[][] arr = new int[word1.length() + 1][word2.length() + 1];
        for (int i = 0; i <= word1.length(); i++) {
            arr[i][0] = i;
        }
        for (int j = 0; j <= word2.length(); j++) {
            arr[0][j] = j;
        }
        for (int i = 0; i < word1.length(); i++) {
            for (int j = 0; j < word2.length(); j++) {
                if (word1.charAt(i) == word2.charAt(j)) {
                    arr[j + 1][i + 1] = arr[j][i];
                } else {
                    int replace = arr[i][j]+1 ;
                    int insert = arr[i][j + 1]+1 ;
                    int delete = arr[i + 1][j]+1 ;

                    int min = replace > insert ? insert : replace;
                    min = delete > min ? min : delete;
                    arr[i + 1][j + 1] = min;
                }
            }
        }
        return arr[word1.length()][word2.length()];
    }

3 实战应用(抽取新闻标题)

这篇文章的第三部分机器学习方法中,在抽取标题时,会计算被h[1-6]标签修饰的字符串和metaTitle之间最相似的一个字符,作为标题。
具体的代码也有展示,也有讲解。

posted @ 2019-09-03 10:09  红色超人  阅读(681)  评论(0编辑  收藏  举报