编辑距离-Levenshtein距离

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;


/**
 * 编辑距离,又称Levenshtein距离,是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。
 * 许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
 * 
 * 俄罗斯科学家Vladimir Levenshtein在1965年提出这个概念。
 * 
 * 问题:找出字符串的编辑距离,即把一个字符串s1最少经过多少步操作变成字符串s2? (操作有三种,添加一个字符,删除一个字符,修改一个字符)
 * 
 * 解析:首先定义这样一个函数——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。

 *
 * 两比较字符串的初始二维距离表:
  		0 	f 	a 	i 	l 	i 	n 	g
 *	0 	  	  	  	  	  	  	  	 
 *	s 	  	  	  	  	  	  	  	 
 *	a 	  	  	  	  	  	  	  	 
 *	i 	  	  	  	  	  	  	  	 
 *	l 	  	  	  	  	  	  	  	 
 *	n 	  	  	  	  	  	  	  	 
 * 
 *  经过①②③步动态规划处理后二维距离表: 	
 *  	0 	f 	a 	i 	l 	i 	n 	g
 *	0 	0 	1 	2 	3 	4 	5 	6 	7
 *	s 	1 	  	  	  	  	  	  	 
 *	a 	2 	  	  	  	  	  	  	 
 *	i 	3 	  	  	  	  	  	  	 
 *	l 	4 	  	  	  	  	  	  	 
 *	n 	5 	  	  
 *
 *	带入动态规划计算 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。 
 *	依次类推有:	
 *
 *    	0 	f 	a 	i 	l 	i 	n 	g
	0 	0 	1 	2 	3 	4 	5 	6 	7
	s 	1 	1 	2 	3 	4 	5 	6 	7
	a 	2 	2 	1 	2 	3 	4 	5 	6
	i 	3 	3 	2 	1 	2 	3 	4 	5
	l 	4 	4 	3 	2 	1 	2 	3 	4
	n 	5 	5 	4 	3 	2 	2 	2 	3	  	  	  	 
 * 
 * 编辑距离
 *
 * @author pjm0008
 * 
 * @version 1.0 bate
 *
 * 2015年12月31日
 */
public class LevensheinDistance {
	
	public static void main(String [] args) {
		String a = readFile("D:/c.txt");
		String b = readFile("D:/d.txt");
		levensDist(a,b);
		String c = "http://www.163.com";
		String d = "http://www.126.com";
		levensDist(c,d);
	}
	
	public static void levensDist (String a, String b) {
		int max = max(a.length(),b.length());
		int x = edit(a,b);
		int y = (max-x)*100/max;
		System.out.println("所需编辑次数:" + x + ",相似度:" + y + "%");
	}
	
	public static int min (int a, int b) {
		return a < b ? a : b;
	}
	
	public static int max (int a, int b) {
		return a > b ? a : b;
	}
	
	public static int edit (String a, String b) {
		
		int len1 = a.length();
		int len2 = b.length();
		
		int [][] matrix = new int[len1+1][len2+1];
		
		for (int i = 0; i < len1 + 1 ; i ++) {
			matrix[i][0] = i;
		}
		
		for (int j = 0; j < len2 + 1; j ++) {
			matrix[0][j] = j;
		}
		
		for (int k = 1; k < len1 + 1; k ++) {
			for (int x = 1; x < len2 + 1; x ++) {
				int f = 1;
				if (a.charAt(k-1) == b.charAt(x-1)) {
					f = 0;
				}
				matrix[k][x] = min(min(matrix[k-1][x]+1,matrix[k][x-1]+1), 
						matrix[k-1][x-1] + f);
			}
		}
		
		return matrix[len1][len2];
	}
	
	public static String readFile (String file) {
		if (null == file || !(new File(file)).exists()) {
			return null;
		}
		String tmp = null;
		StringBuffer sb = new StringBuffer();
		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader(file));
			while ((tmp = br.readLine()) != null) {
				sb.append(tmp).append(System.lineSeparator());
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		return sb.toString();
	}

}
点赞