使用动态规划求2个字符串的最短编辑距离并输出编辑步骤 Java 实现

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

你可能感兴趣的:(算法,动态规划,算法可视化,java,动态规划,数据结构,算法,字符串相似度)