題目描述
Levenshtein 距離,又稱編輯距離,指的是兩個字符串之間,由一個轉換成另外一個所需的最少編輯操做次數。許可的編輯操做包括將一個字符替換成另外一個字符,插入一個字符,刪除一個字符。編輯距離的算法是首先由俄國科學家Levenshtein提出的,故又叫Levenshtein Distance。
Ex:
字符串A:abcdefg
字符串B: abcdef
經過增長或是刪掉字符」g」的方式達到目的。這兩種方案都須要一次操做。把這個操做所須要的次數定義爲兩個字符串的距離。
要求:
給定任意兩個字符串,寫出一個算法計算它們的編輯距離。
請實現以下接口
/**
* 功能:計算兩個字符串的距離
* 輸入:字符串A和字符串B
* 輸出:無
* 返回:若是成功計算出字符串的距離,不然返回-1
*/
public static int stringDistance (String charA, String charB){
return 0;
}
輸入描述
輸入兩個字符串
輸出描述
獲得計算結果
輸入例子
abcdefg
abcdef
輸出例子
1
算法實現
import java.util.Scanner;
/**
* Declaration: All Rights Reserved !!!
*/
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Scanner scanner = new Scanner(Main.class.getClassLoader().getResourceAsStream("data.txt"));
while (scanner.hasNext()) {
String a = scanner.nextLine();
String b = scanner.nextLine();
System.out.println(stringDistance(a, b));
}
scanner.close();
}
private static int stringDistance(String a, String b) {
// System.out.println(stringDistance(a.toCharArray(), 0, b.toCharArray(), 0));
return stringDistance(a.toCharArray(), b.toCharArray());
}
/**
* 方法1、計算量過大
* <pre>
* 兩個字符串的距離確定不超過它們的長度之和(咱們能夠經過刪除操做把兩個串都轉化爲空串)。
* 雖然這個結論對結果沒有幫助,但至少能夠知道,任意兩個字符串的距離都是有限的。
* 咱們仍是應該集中考慮如何才能把這個問題轉化成規模較小的一樣的問題。
* 若是有兩個串A=xabcdae和B=xfdfa,它們的第一個字符是相同的,只要計算A[2,…,7]=abcdae
* 和B[2,…,5]=fdfa的距離就能夠了。可是若是兩個串的第一個字符不相同,
* 那麼能夠進行以下的操做(lenA和lenB分別是A串和B串的長度):
* 1.刪除A串的第一個字符,而後計算A[2,…,lenA]和B[1,…,lenB]的距離。
* 2.刪除B串的第一個字符,而後計算A[1,…,lenA]和B[2,…,lenB]的距離。
* 3.修改A串的第一個字符爲B串的第一個字符,而後計算A[2,…,lenA]和B[2,…,lenB]的距離。
* 4.修改B串的第一個字符爲A串的第一個字符,而後計算A[2,…,lenA]和B[2,…,lenB]的距離。
* 5.增長B串的第一個字符到A串的第一個字符以前,而後計算A[1,…,lenA]和B[2,…,lenB]的距離。
* 6.增長A串的第一個字符到B串的第一個字符以前,而後計算A[2,…,lenA]和B[1,…,lenB]的距離。
*
* 在這個題目中,咱們並不在意兩個字符串變得相等以後的字符串是怎樣的。因此,能夠將上面6個操做合併爲:
* 1.一步操做以後,再將A[2,…,lenA]和B[1,…,lenB]變成相同字符串。
* 2.一步操做以後,再將A[1,…,lenA]和B[2,…,lenB]變成相同字符串。
* 3.一步操做以後,再將A[2,…,lenA]和B[2,…,lenB]變成相同字符串。
* </pre>
*
* @param a
* @param i
* @param b
* @param j
* @return
*/
private static int stringDistance(char[] a, int i, char[] b, int j) {
if (i >= a.length || j >= b.length) {
return Math.max(a.length - i, b.length - j);
}
// 字符相等
if (a[i] == b[j]) {
return stringDistance(a, i + 1, b, j + 1);
} else {
int d1 = stringDistance(a, i + 1, b, j);
int d2 = stringDistance(a, i + 1, b, j + 1);
int d3 = stringDistance(a, i, b, j + 1);
return Math.min(Math.min(d1, d2), d3) + 1;
}
}
/**
* 方法二
* <pre>
* 很經典的可以使用動態規劃方法解決的題目,和計算兩字符串的最長公共子序列類似。
*
* 設Ai爲字符串A(a1a2a3 … am)的前i個字符(即爲a1,a2,a3 … ai)
* 設Bj爲字符串B(b1b2b3 … bn)的前j個字符(即爲b1,b2,b3 … bj)
*
* 設 L(i,j)爲使兩個字符串和Ai和Bj相等的最小操做次數。
* 當ai==bj時 顯然 L(i,j) = L(i-1,j-1)
* 當ai!=bj時
*
* 若將它們修改成相等,則對兩個字符串至少還要操做L(i-1,j-1)次
* 若刪除ai或在bj後添加ai,則對兩個字符串至少還要操做L(i-1,j)次
* 若刪除bj或在ai後添加bj,則對兩個字符串至少還要操做L(i,j-1)次
* 此時L(i,j) = min( L(i-1,j-1), L(i-1,j), L(i,j-1) ) + 1
*
* 顯然,L(i,0)=i,L(0,j)=j, 再利用上述的遞推公式,能夠直接計算出L(i,j)值。
* </pre>
*
* @param a
* @param b
* @return
*/
private static int stringDistance(char[] a, char[] b) {
int[][] len = new int[a.length + 1][b.length + 1];
for (int i = 0; i < len.length; i++) {
len[i][0] = i;
}
for (int j = 0; j < len[0].length; j++) {
len[0][j] = j;
}
for (int i = 1; i < len.length; i++) {
for (int j = 1; j < len[0].length; j++) {
if (a[i - 1] == b[j - 1]) {
len[i][j] = len[i - 1][j - 1];
} else {
len[i][j] = Math.min(Math.min(len[i - 1][j], len[i - 1][j - 1]), len[i][j - 1]) + 1;
}
}
}
return len[len.length - 1][len[0].length - 1];
}
}