StringEditDistanceTest.java:
import java.util.ArrayList;
import java.util.List;
public class StringEditDistanceTest {
private static final String str1 = "mother";
private static final String str2 = "monster";
private static final List characters1 = string2CharacterList( str1 );
private static final List characters2 = string2CharacterList( str2 );
public static void main(String[] args) {
MinimumTransferWayVO way = calculateMinimumTransferWay(characters1, characters2);
printOpeartions( way.getOperations() );
}
private static List string2CharacterList(String str) {
List characters = new ArrayList<>();
int length = str.length();
for (int i = 0; i < length; i++) {
char c = str.charAt(i);
characters.add( c );
}
return characters;
}
/**
* ReadOperationVO 不贡献操作次数
* @param operations
*/
private static int getOpeartionCount(List operations) {
int count = 0;
for( OperationVO operation:operations ){
if( operation instanceof ReadOperationVO ){
// ReadOperationVO 不贡献操作次数
}else {
count++;
}
}
return count;
}
private static void printOpeartions(List operations) {
System.out.println( str1 + " --> " + str2 + ":" );
for( OperationVO operation:operations ){
if( operation instanceof ReadOperationVO ){
ReadOperationVO readOperation = (ReadOperationVO) operation;
System.out.println( " " + characters1.get( readOperation.getIndex1() ) );
}else if( operation instanceof InsertOperationVO ){
InsertOperationVO insertOperation = (InsertOperationVO) operation;
System.out.println( "添加 " + characters2.get( insertOperation.getIndex2() ) );
}else if( operation instanceof DeleteOperationVO ){
DeleteOperationVO deleteOperation = (DeleteOperationVO) operation;
System.out.println( "删除 " + characters1.get( deleteOperation.getIndex1() ) );
}else if( operation instanceof EditOperationVO ){
EditOperationVO editOperation = (EditOperationVO) operation;
System.out.println( "修改 " + characters1.get( editOperation.getIndex1() ) + " 为 " + characters2.get( editOperation.getIndex2() ) );
}
}
}
private static MinimumTransferWayVO calculateMinimumTransferWay( List characters1,List characters2 ){
// dp[i][j] 表示的是将 characters1 的前i个元素变换为 characters2 中的前j个元素需要使用的最优( 即需要转换步骤最少 )的转换方式
int size_v1 = characters1.size();
int size_v2 = characters2.size();
MinimumTransferWayVO[][] dp = new MinimumTransferWayVO[ size_v1 ][ size_v2 ];
for (int index1 = 0; index1 < size_v1; index1++) {
Character char1 = characters1.get( index1 );
for (int index2 = 0; index2 < size_v2; index2++) {
Character char2 = characters2.get( index2 );
MinimumTransferWayVO minimumTransferWay = new MinimumTransferWayVO();
if( index1 == 0 ){
if( index2 == 0 ){
// v1 和 v2 都只有 1 个字符,此时最简单,只需要检测这 1 个字符是否相同?
/*
v1 v2
a ==》 a
*/
List operations = new ArrayList<>();
if( char1.equals( char2 ) ){
// 相同,不需要任何变换,放置一个 ReadOperationVO 方便输出转换过程
ReadOperationVO readOperation = new ReadOperationVO();
readOperation.setIndex1( index1 );
readOperation.setIndex2( index2 );
operations.add( readOperation );
}else {
// 不相同,需要 1 步修改操作
EditOperationVO editOperation = new EditOperationVO();
editOperation.setIndex1( index1 );
editOperation.setIndex2( index2 );
operations.add( editOperation );
}
minimumTransferWay.setOperations( operations );
}else {
// v1只有1行文本,v2有多行文本,此时的变换也比较简单,
// 检测 line_v1 在 lines_v2 中是否存在
List operations = new ArrayList<>();
if( contains( characters2,char1,0,index2 ) ){
// line_v1 在 lines_v2 中存在
/*
v1 v2
1111111111111111111111 ==》 2222222222222222222222
1111111111111111111111
444444444444444444444444
555555555555555555
如下一种情形,v1转换为v2需要在 "1111111111111111111111" 上面插入一行 "2222222222222222222222",
然后在 "1111111111111111111111" 下面插入 "444444444444444444444444"、"555555555555555555" 行
*/
// firstEqualIndex 介于 0 和 lineNum_v2 之间
int firstEqualIndex = getFirstEqualIndex(characters2, char1, 0, index2);
for (int lineNum = 0; lineNum <=index2 ; lineNum++) {
if( lineNum == firstEqualIndex ){
ReadOperationVO readOperation = new ReadOperationVO();
readOperation.setIndex1( index1 );
readOperation.setIndex2( lineNum );
operations.add( readOperation );
}else {
// 插入行
InsertOperationVO insertOperation = new InsertOperationVO();
insertOperation.setIndex2( lineNum );
operations.add( insertOperation );
}
}
}else {
// line_v1 在 lines_v2 中不存在
/*
v1 v2
1111111111111111111111 ==》 2222222222222222222222
3333333333333333333
444444444444444444444444
555555555555555555
此时,v1 转换成 v2,需要先删除 "1111111111111111111111" 行,
再插入 "2222222222222222222222"、
"3333333333333333333"、
"444444444444444444444444"、
"555555555555555555" 行
*/
DeleteOperationVO deleteOperation = new DeleteOperationVO();
deleteOperation.setIndex1( index1 );
operations.add( deleteOperation );
for (int lineNum = 0; lineNum <= index2; lineNum++) {
InsertOperationVO insertOperation = new InsertOperationVO();
insertOperation.setIndex2( lineNum );
operations.add( insertOperation );
}
}
minimumTransferWay.setOperations( operations );
}
}else {
if( index2 == 0 ){
// v1有多行文本,v1只有1行文本,
List operations = new ArrayList<>();
// 检测 line_v2 是否在 lines_v1 中存在
if( contains(characters1, char2, 0, index1) ){
// line_v2 在 lines_v1 中存在
/*
v1 v2
11111111111111 333333333333
333333333333
444444444444444
5555555555
*/
// 此时,v1转换为 v2需要删除 "333333333333" 上面的 "11111111111111"行,删除 "333333333333" 下面的 "444444444444444"、"5555555555" 行
int firstEqualIndex = getFirstEqualIndex(characters1, char2, 0, index1);
for (int index = 0; index <=index1 ; index++) {
if( index == firstEqualIndex){
ReadOperationVO readOperation= new ReadOperationVO();
readOperation.setIndex1( index );
readOperation.setIndex2( index2 );
operations.add( readOperation );
}else {
// 删除行
DeleteOperationVO deleteOperation = new DeleteOperationVO();
deleteOperation.setIndex1( index );
operations.add( deleteOperation );
}
}
}else {
// line_v2 在 lines_v1 中不存在
/*
v1 v2
11111111111111 22222222222222
333333333333
444444444444444
5555555555
*/
// 此时,v1 转换为 v2 需要删除 "11111111111111"、 "333333333333"、 "444444444444444"、 "5555555555",再新增 "22222222222222"
for (int index = 0; index <=index1 ; index++) {
// 删除行
DeleteOperationVO deleteOperation = new DeleteOperationVO();
deleteOperation.setIndex1( index );
operations.add( deleteOperation );
}
// 插入行
InsertOperationVO insertOperation = new InsertOperationVO();
insertOperation.setIndex2( index2 );
operations.add( insertOperation );
}
minimumTransferWay.setOperations( operations );
}else {
List operations = new ArrayList<>();
// v1 和 v2 都有多行文本,最复杂。
// 检测 v1 的 v2 的当前行是否相同
if( char1.equals( char2 ) ){
// line_v1 和 line_v2 相同
/*
v1 v2
11111111 1111111
22222222 33333
... ...
44444444 555555
66666666 66666666
*/
// 如上所示,v1的当前行 "66666666" 和 v2 的当前行 "66666666" 相同,只需要看 v1 点位前面部分 转换wield v2的前面部分的最小转换方式就行了,
MinimumTransferWayVO minimumTransferWay_prev = dp[index1 - 1][index2 - 1];
// todo 是否需要深拷贝
operations.addAll( minimumTransferWay_prev.getOperations() );
// 追加一个读操作,方便输出转换过程
ReadOperationVO readOperation = new ReadOperationVO();
readOperation.setIndex1( index1 );
readOperation.setIndex2( index2 );
operations.add( readOperation );
}else {
// line_v1 和 line_v2 不相同
/*
v1 v2
11111111 1111111
22222222 33333
... ...
44444444 555555
66666666 7777777
*/
// 如上所示,v1 的当前行 "66666666" 和 v2 的当前行 "7777777" 不相同,则有3个候选转换方式,需要从中选择最小的转换方式
// 1. v1 的前面部分 转换为 v2 的前面部分 + v2 的当前行部分,删除 v1 的当前行 "66666666"
MinimumTransferWayVO minimumTransferWay_prev1 = dp[index1 - 1][index2];
int opeartionCount_1 = getOpeartionCount(minimumTransferWay_prev1.getOperations());
// 2. v1 的前面部分 + v1 的当前行部分 转换为 v2 的前面部分,v2 新增当前行 "7777777"
MinimumTransferWayVO minimumTransferWay_prev2 = dp[index1 ][index2 - 1];
int opeartionCount_2 = getOpeartionCount(minimumTransferWay_prev2.getOperations());
// 3. v1 的前面部分转换为 v2的前面部分,v1的 当前行 "66666666" 修改为 v2 的当前行 "7777777"
MinimumTransferWayVO minimumTransferWay_prev3 = dp[index1 - 1][index2 - 1];
int opeartionCount_3 = getOpeartionCount(minimumTransferWay_prev3.getOperations());
boolean isOne = true;
boolean isTwo = false;
boolean isThree = false;
MinimumTransferWayVO minimumTransferWay_prev = minimumTransferWay_prev1;
int opeartionCount = opeartionCount_1;
if( opeartionCount_2 < opeartionCount ){
minimumTransferWay_prev = minimumTransferWay_prev2;
opeartionCount = opeartionCount_2;
isOne = false;
isTwo = true;
isThree = false;
}
if( opeartionCount_3 < opeartionCount ){
minimumTransferWay_prev = minimumTransferWay_prev3;
opeartionCount = opeartionCount_3;
isOne = false;
isTwo = false;
isThree = true;
}
operations.addAll( minimumTransferWay_prev.getOperations() );
if( isOne ){
// 删除 v1 的当前行 "66666666"
DeleteOperationVO deleteOperation = new DeleteOperationVO();
deleteOperation.setIndex1( index1 );
operations.add( deleteOperation );
}else if( isTwo ){
// v2 新增当前行 "7777777"
InsertOperationVO insertOperation = new InsertOperationVO();
insertOperation.setIndex2( index2 );
operations.add( insertOperation );
}else if( isThree ){
// v1的 当前行 "66666666" 修改为 v2 的当前行 "7777777"
EditOperationVO editOperation = new EditOperationVO();
editOperation.setIndex1( index1 );
editOperation.setIndex2( index2 );
operations.add( editOperation );
}
}
minimumTransferWay.setOperations( operations );
}
}
dp[ index1 ][ index2 ] = minimumTransferWay;
}
}
return dp[ size_v1 -1 ][ size_v2 -1 ];
}
/**
* @param characters
* @param targetChar
* @param beginIndex
* @param endIndex
* @return
*/
private static boolean contains(List 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;
}
private static int getFirstEqualIndex(List characters, Character targetChar, int beginIndex, int endIndex) {
for (int i = beginIndex; i <=endIndex ; i++) {
// todo 是 == 还是 equals???
if( targetChar.equals( characters.get( i ) ) ){
return i;
}
}
return -1;
}
}
MinimumTransferWayVO.java:
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.util.List;
@Getter
@Setter
public class MinimumTransferWayVO implements Serializable {
private List operations;
}
OperationVO.java:
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
@Getter
@Setter
public class OperationVO implements Serializable {
}
InsertOperationVO.java:
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
@Getter
@Setter
public class InsertOperationVO extends OperationVO {
private int index2;
}
DeleteOperationVO.java:
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
@Getter
@Setter
public class DeleteOperationVO extends OperationVO {
private int index1;
}
EditOperationVO.java:
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class EditOperationVO extends OperationVO {
private int index1;
private int index2;
}
ReadOperationVO.java:
import lombok.Getter;
import lombok.Setter;
/**
* ReadOperationVO 不贡献操作步数,只是为了方便输出v1到v2的整个变换过程
*
**/
@Getter
@Setter
public class ReadOperationVO extends OperationVO {
// 此时 lines_v1[ lineNum_v1 ] 和 lines_v2[ lineNum_v2 ] 相同
private int index1;
private int index2;
}
测试输出:
mother --> monster:
m
o
添加 n
添加 s
t
删除 h
e
r
receiveDataService --> receviceService:
r
e
c
e
添加 v
i
修改 v 为 c
e
删除 D
删除 a
删除 t
删除 a
S
e
r
v
i
c
e
motherfuck --> monstersucker:
m
o
添加 n
添加 s
t
删除 h
e
r
修改 f 为 s
u
c
k
添加 e
添加 r