短文本相似度:编辑距离算法及其应用

最近因为在做短文本字符串相似度比较的事情,重温了一下编辑距离算法及其应用。

一、概念:

编辑距离,又称Levenshtein距离,是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。

许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。

例如将kitten一字转成sitting:

  1. kitten(k→s)
  2. sitten(e→i)
  3. sittin(+g)
  4. sitting

俄罗斯科学家Vladimir Levenshtein在1965年提出这个概念。

二、算法:

问题:找出字符串的编辑距离,即把一个字符串s1最少经过多少步操作变成编程字符串s2,操作有三种,添加一个字符,删除一个字符,修改一个字符

解析:首先定义这样一个函数(矩阵)——d(i, j),它表示第一个字符串的长度为i的子串到第二个字符串的长度为j的子串的编辑距离。

算法过程:

  1. str1或str2的长度为0返回另一个字符串的长度。 if(str1.length==0) return str2.length; if(str2.length==0) return str1.length;
  2. 初始化(n+1)*(m+1)的矩阵d,并让第一行和列的值从0开始增长;
  3. 扫描两字符串(n*m级的),如果:str1[i] == str2[j],用temp记录它,为0。否则temp记为1。然后在矩阵d[i,j]赋于d[i-1,j]+1 、d[i,j-1]+1、d[i-1,j-1]+temp三者的最小值;
  4. 扫描完后,返回矩阵的最后一个值d[n][m]即是它们的距离。

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

通过算法过程伪码我们不难发现,算法其实是一种动态规划。因此理论上使用暴力算法一遍一遍地扫描字符串也可以解出,但逻辑过于复杂,动态规划思想很好地解决了这个问题。

三、代码实现

package tools;

public class EditDistance {

	private int[][] array;
	private String str1;
	private String str2;

	public EditDistance(String str1, String str2) {
		this.str1 = str1;
		this.str2 = str2;
	}

	public int edit() {
		int max1 = str1.length();
		int max2 = str2.length();
		// 建立数组,比字符长度大一个空间
		array = new int[max2 + 1][max1 + 1];
		for (int i = 0; i <= max1; i++) {
			array[0][i] = i;
		}
		for (int j = 0; j <= max2; j++) {
			array[j][0] = j;
		}

		for (int i = 1; i <= max1; i++) {
			for (int j = 1; j <= max2; j++) {
				array[j][i] = levenshtein(i, j, str1.charAt(i - 1), str2.charAt(j - 1));
			}
		}
		return array[max2][max1];
	}

	public int levenshtein(int i, int j, char si, char sj) {
		int result = 0;

		if (i >= 1 && j >= 1) {
			int a = array[j - 1][i] + 1;
			int b = array[j][i - 1] + 1;
			int c = array[j - 1][i - 1] + ((si != sj) ? 2 : 0);
			result = min(a, b, c);
		}
		return result;
	}

	public int min(int a, int b, int c) {
		int temp = a < b ? a : b;
		return temp < c ? temp : c;
	}

	// 计算相似度
	public float similarity() {
		float similarity = 1 - (float) array[str2.length()][str1.length()] / Math.max(str1.length(), str2.length());
		return similarity;
	}

	public static void main(String args[]) {
		String str1 = "首选的确诊方法是()";
		String str2 = "首选确诊方法是";
		EditDistance lt = new EditDistance(Tools.cleanTitle(str1), Tools.cleanTitle(str2));
		System.out.println(lt.edit());
		System.out.println(lt.similarity());
	}
}

测试结果

3
0.7

四、应用

  • DNA分析
  • 拼字检查
  • 语音辨识
  • 抄袭侦测
  • 搜索引擎

猜你喜欢

转载自blog.csdn.net/u012998680/article/details/113404323