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

定义

编辑距离又称Leveinshtein距离,是由俄罗斯科学家Vladimir Levenshtein在1965年提出。
以字符串为例,字符串a和字符串b的编辑距离是将a转换成b的最小操作次数,这里的操作包括三种:
插入一个字符
删除一个字符
替换一个字符

举个例子,
计算learning和meaning的编辑距离,需要下列步骤

  • learning -> mearning 将k替换成s
  • mearning -> meaning 将r删除

至少要做2次操作,编辑距离为2

公式

e d a , b ( i , j ) ed_{a,b}(i,j) eda,b(i,j)来表示a,b字符串的编辑距离,其中i代表a的长度,j代表b的长度
e d a , b ( i , j ) = { m i n ( i , j ) = 0 max ⁡ ( i , j ) a i = b j e d a , b ( i − 1 , j − 1 ) a i &lt; &gt; b j min ⁡ ( e d a , b ( i − 1 , j − 1 ) + 1 , e d a , b ( i − 1 , j ) + 1 , e d a , b ( i , j − 1 ) + 1 ) ed_{a,b}(i,j)=\left\{ \begin{aligned} min(i,j) =0 &amp;&amp; \max(i,j) \\ a_i = b_j &amp;&amp; ed_{a,b}(i-1,j-1)\\ a_i &lt;&gt; b_j &amp;&amp; \min(ed_{a,b}(i-1,j-1)+1,ed_{a,b}(i-1,j)+1,ed_{a,b}(i,j-1)+1) \end{aligned} \right. eda,b(i,j)=min(i,j)=0ai=bjai<>bjmax(i,j)eda,b(i1,j1)min(eda,b(i1,j1)+1,eda,b(i1,j)+1,eda,b(i,j1)+1)
e d a , b ( i , j ) ed_{a,b}(i,j) eda,b(i,j)的值描述如下:

  • min(i,j)=0 代表有一个字符串为空,编辑距离就是另一个非空字符串的长度
  • 当字符 a i a_i ai值等于 b j b_j bj的时候,编辑距离就是a,b字符串上一个字符的编辑距离 e d a , b ( i − 1 , j − 1 ) ed_{a,b}(i-1,j-1) eda,b(i1,j1)
  • a i a_i ai值不等于 b j b_j bj的时候,编辑距离如下三种情况的最小值
    1. e d a , b ( i − 1 , j ) + 1 ed_{a,b}(i-1,j)+1 eda,b(i1,j)+1 删除 a i a_i ai
    2. e d a , b ( i , j − 1 ) + 1 ed_{a,b}(i,j-1)+1 eda,b(i,j1)+1 插入 b j b_j bj
    3. e d a , b ( i − 1 , j − 1 ) + 1 ed_{a,b}(i-1,j-1)+1 eda,b(i1,j1)+1 替换 b j b_j bj

矩阵

在前面的公式中,我们可以很明显的感觉到可以用一个二维矩阵来表达该距离
我们用learn(a)和mean(b)比较简单的单词用矩阵来表达:

  1. 初始化:在矩阵的第1行的时候,表示b的长度为0,根据第一个规则,当b长度为0的时候,a的编辑距离就是a字符串的长度,也就是mean的1,2,3,4,反之第一列也是如此
0mean
001234
l1
e2
a3
r4
n5
0mean
001234
l1m!=l 所以取min(0+1,1+1,1+1)=1
e2
a3
r4
n5
0mean
001234
l11e!=l 所以取min(1+1,2+1,1+1)=2
e2
a3
r4
n5

如此持续迭代,直至推导最终结果

0mean
001234
l11234
e22123
a33212
r44322
n55432

最后的编辑距离就是2

算法优化

当i<j的时候,最小的时间复杂度O ( i 2 ) (i^2) (i2),最小空间复杂度是O ( 2 ∗ i ) (2*i) (2i)

为何复杂度不是O ( i ∗ j ) (i*j) (ij)?
当我们只是计算编辑距离的时候,只是求矩阵的最后一个值。当字符串的长度i<j的时候,我们并不需要继续计算在区间i到j的距离,因为在这种场景下只是插入字符到尾部,依据定义,只要将(j-i-1)差值直接加上就可,无需在继续计算

为何最小空间复杂度是O ( 2 ∗ i ) (2*i) (2i)
在我们计算的时候,发现只是需要2行也就是当前行和前一行的状态,所以没必要保存完整的矩阵,而只需要保存2行矩阵

优化后的代码如下:

public static int editDistance(String left, String right) {
		if(left.length() > right.length()) {
			String tmp = left;
			left = right;
			right = tmp;
		}
		int x,y,gap = 0;
		if(right.length() == left.length()) {
	    	 x = left.length();
	    	 y = x;
	    } else {
	    	x = left.length()+1;
	    	y = left.length();
	    	gap = right.length() - left.length() -1;
	    }
	    int[] pre = new int[ y + 1];
	    int[] cur = new int[ y + 1];
	    for (int i = 0; i < pre.length; i++) {
	        pre[i] = i;
	    }
	    for (int i = 1; i <= x; i++) {
	    	cur[0] = i;
	        for (int j = 1; j <= y; j++) {
	            if (right.charAt(i - 1) == left.charAt(j - 1)) {
	            	cur[j] = pre[j - 1];
	            } else {
	            	cur[j] = Math.min(cur[j - 1], Math.min(pre[j], pre[j - 1])) + 1;
	            }
	        }
	        int[] tmp = pre;
	        pre = cur;
	        cur = tmp;
	    }
	    return pre[pre.length - 1]+gap;
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值