StringEditDistanceTest.java:
import java.util.ArrayList;
import java.util.List;
public class StringEditDistanceTest {
private static final String str1 = "form";
private static final String str2 = "from";
private static final List<Character> characters_v1 = string2CharacterList( str1 );
private static final List<Character> characters_v2 = string2CharacterList( str2 );
public static void main(String[] args) {
int[][] dp = calculateShortestEditDistance(characters_v1, characters_v2);
int index1 = characters_v1.size() - 1;
int index2 = characters_v2.size() - 1;
List<String> results = new ArrayList<>();
while ( index1 >= 0 && index2 >= 0 ){
Character char1 = characters_v1.get(index1);
Character char2 = characters_v2.get(index2);
if( char1.equals( char2 ) ){
// v1:...a
// v2:...a
// 将 char1 和char2相同,原封不动的输出
results.add( " " + char1 );
index1--;
index2--;
}else {
// v1:...a
// v2:...b
// v1:... a
// v2:... b
// 此时,a修改修改为b
int sed1 = dp[index1 - 1][index2 - 1];
// v1:...a
// v2: ... b
// 此时,需要插入b
int sed2 = dp[index1][index2 - 1];
// v1: ... a
// v2:...b
// 此时,需要删除a
int sed3 = dp[index1-1][index2];
int sed = Math.min( Math.min( sed1,sed2 ),sed3 );
if( sed == sed1 ){
results.add( " " + char1 + " --> " + char2 );
index1--;
index2--;
}else if( sed == sed2 ){
results.add( "+ " + char2 );
index2--;
}else if( sed == sed3 ){
results.add( "- " + char1 );
index1--;
}
}
}
while ( index1 >= 0 ){
// v1 中多出的 "首行们" 都是需要删除的
results.add( "- " + characters_v1.get( index1 ) );
index1--;
}
while ( index2 >= 0 ){
// v2 中多出的 "首行们" 都是需要被插入的
results.add( "+ " + characters_v2.get( index2 ) );
index2--;
}
System.out.println( str1 );
System.out.println( str2 );
for (int i=results.size() -1;i>=0;i--){
System.out.println( results.get( i ) );
}
}
private static List<Character> string2CharacterList(String str) {
List<Character> characters = new ArrayList<>();
int length = str.length();
for (int i = 0; i < length; i++) {
char c = str.charAt(i);
characters.add( c );
}
return characters;
}
private static int[][] calculateShortestEditDistance( List<Character> characters_v1,List<Character> characters_v2 ){
// dp[i][j] 表示的是将 characters1 的前i个元素变换为 characters2 中的前j个元素需要使用的最优( 即需要转换步骤最少 )的转换方式
int size_v1 = characters_v1.size();
int size_v2 = characters_v2.size();
int[][] dp = new int[ size_v1 ][ size_v2 ];
for (int index1 = 0; index1 < size_v1; index1++) {
Character char1 = characters_v1.get( index1 );
for (int index2 = 0; index2 < size_v2; index2++) {
Character char2 = characters_v2.get( index2 );
if( index1 == 0 ){
if( index2 == 0 ){
if( char1.equals( char2 ) ){
// v1:a
// v2:a
dp[ index1 ][ index2 ] = 0;
}else {
// v1:a
// v2:b
dp[ index1 ][ index2 ] = 1;
}
}else {
if( contains( characters_v2,char1,0,index2 ) ){
// v1: a
// v2:...a... size = index2 + 1
// v1转换为 v2需要 size - 1步( 也就是 index2步 )插入操作
dp[ index1 ][ index2 ] = index2;
}else {
// v1: a
// v2:...b... size = index2 + 1
// v1转换为 v2需要 1步编辑操作,size-1= index2 步插入操作,一共index2 + 1步操作
dp[ index1 ][ index2 ] = index2 + 1;
}
}
}else {
if( index2 == 0 ){
if( contains(characters_v1, char2, 0, index1) ){
// v1:....a... size = index1 + 1
// v2: a
// v1转换为 v2需要 size-1=index1步删除操作
dp[ index1 ][ index2 ] = index1;
}else {
// v1:....a... size = index1 + 1
// v2: b
// v1转换为 v2需要 1步编辑操作和size-1=index1步删除操作,一共index1+1步操作
dp[ index1 ][ index2 ] = index1 + 1;
}
}else {
if( char1.equals( char2 ) ){
// v1:...a
// v2:...a
dp[ index1 ][ index2 ] = dp[ index1 - 1 ][ index2 - 1 ];
}else {
// v1:...a
// v2:...b
// v1:... a
// v2:... b
// 此时 v1 的前部分和 v2的前部分做dp运算,a修改为b
int sed_prev1 = dp[ index1 - 1 ][ index2 - 1 ];
// v1: ... a
// v2:...b
// 此时v1的前部分和v2做dp运算,删除a
int sed_prev2 = dp[ index1 - 1 ][ index2 ];
// v1: ...a
// v2: ... b
// 此时 v1和v2的前部分做dp运算,插入b
int sed_prev3 = dp[ index1 ][ index2 - 1 ];
int sed = Math.min( Math.min( sed_prev1,sed_prev2 ),sed_prev3 ) + 1;
dp[ index1 ][ index2 ] = sed;
}
}
}
}
}
return dp;
}
/**
* @param characters
* @param targetChar
* @param beginIndex
* @param endIndex
* @return
*/
private static boolean contains(List<Character> characters, Character targetChar, int beginIndex, int endIndex) {
for (int i = beginIndex; i <=endIndex ; i++) {
// todo 是 == 还是 equals???
if( targetChar.equals( characters.get( i ) ) ){
return true;
}
}
return false;
}
}
测试输出:
form
from
f
o --> r
r --> o
m
mother
monster
m
o
+ n
t --> s
h --> t
e
r
一共需要8步编辑操作
StringEditDistanceTest
testStringEditDistance
+ t
+ e
+ s
+ t
S
t
r
i
n
g
E
d
i
t
D
i
s
t
a
n
c
- e
- T
e
- s
- t