EditStepInfo.java:


import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;
import java.util.List;


@Getter
@Setter
public class EditStepInfo implements Serializable {

    private String str1;
    private String str2;

    // str1和 str2 的最短编辑路基
    private int sed;

    private List<StepVO> steps;


    private String tempString;


}
StepVO.java:


import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;


@Getter
@Setter
public class StepVO implements Serializable {

    public static final String OPT_TYPE_ADD = "add";
    public static final String OPT_TYPE_DELETE = "delete";
    public static final String OPT_TYPE_UPDATE = "UPDATE";

    /**
     * 当前变换描述
     */
    private String desc;

    private int optIndex;

    private Character optChar1;
    private Character optChar2;

    private String optType;

    /**
     * 当前变换成的字符串
     */
    private String tempString;

    public static void main(String[] args) {
        String s = "abcdf";
        Character c = s.charAt(0);
        System.out.println( c.toString() );
    }

}
ShortestEditDistanceTest.java:


import java.util.ArrayList;
import java.util.List;


public class ShortestEditDistanceTest {

    private static EditStepInfo[][] MAT_SED = null;

    public static void main(String[] args) {
        //   m o t h e r
        //   m o n s t e r
        String string1 = "calculate";
        String string2 = "caketakes";
        MAT_SED = new EditStepInfo[ string1.length() ][ string2.length() ];
        calculateShortestEditDistance(string1, string2);

        for( int i=0;i<string1.length();i++ ) {
            for (int j = 0; j < string2.length(); j++) {
                EditStepInfo editStepInfo = MAT_SED[i][j];
                printEditStepInfo( editStepInfo );
                System.out.println();
                System.out.println();
            }
        }
    }

    private static void printEditStepInfo(EditStepInfo editStepInfo) {
        System.out.println( editStepInfo.getStr1() + " 到 " + editStepInfo.getStr2() + " 的最小编辑距离:" + editStepInfo.getSed() );
        System.out.println( "编辑步骤:" );
        List<StepVO> steps = editStepInfo.getSteps();
        if( steps == null || steps.size() == 0 ){
            System.out.println( "\ttempString = " + editStepInfo.getTempString() );
        }else {
            String tempString = editStepInfo.getStr1();
            for( StepVO step:steps ){
                String optType = step.getOptType();
                int optIndex = step.getOptIndex();
                Character optChar1 = step.getOptChar1();
                if( StepVO.OPT_TYPE_ADD.equals( optType ) ){
                    // 新增
                    // 0 1 2 3 4
                    tempString = addChar2String( tempString,optChar1,optIndex );
                }else if( StepVO.OPT_TYPE_DELETE.equals( optType ) ){
                    // 删除
                    // tempString = removeCharacter( tempString,optIndex );
                    tempString = updateCharacterInString( tempString,optIndex,' ' );
                }else if( StepVO.OPT_TYPE_UPDATE.equals( optType ) ){
                    // 修改
                    Character optChar2 = step.getOptChar2();
                    tempString = updateCharacterInString( tempString,optIndex,optChar2 );
                }
                System.out.println( "\t" + step.getDesc() + ":" + tempString  + " optIndex = " + optIndex);
            }
        }
    }

    private static String updateCharacterInString(String str, int updateIndex, Character character) {
        // 01234
        if( updateIndex < 0  ){
            updateIndex = 0;
        }else if( updateIndex >= str.length() ){
            updateIndex = str.length() - 1;
        }
        int length = str.length();
        String str_result = "";
        for( int i=0;i<length;i++ ){
            char c = str.charAt(i);
            if( i == updateIndex ){
                str_result += character;
            }else {
                str_result += c;
            }
        }
        return str_result;
    }

    private static String removeCharacter(String str,int removeIndex) {
        // 01234
        if( removeIndex < 0 ){
            removeIndex = 0;
        }
        if( removeIndex >= str.length() ){
            removeIndex = str.length() - 1;
        }
        if( removeIndex == 0 ){
            return str.substring( 1 );
        }else if( removeIndex == str.length() - 1 ){
            return str.substring( 0,removeIndex );
        }else {
            return str.substring( 0,removeIndex  ) + str.substring( removeIndex + 1 );
        }
    }

    private static String addChar2String(String str,Character character, int addIndex) {
        if( addIndex > str.length() ){
            addIndex = str.length();
        }
        return str.substring( 0,addIndex ) + character + str.substring( addIndex );
    }

    private static EditStepInfo calculateShortestEditDistance( String string1,String string2 ){
        if( string1 == null || string1.length() == 0 ){
            EditStepInfo editStepInfo = new EditStepInfo();
            editStepInfo.setStr1( string1 );
            editStepInfo.setStr2( string2 );
            editStepInfo.setSed( string2.length() );
            return editStepInfo;
        }
        if( string2 == null || string2.length() == 0){
            EditStepInfo editStepInfo = new EditStepInfo();
            editStepInfo.setStr1( string1 );
            editStepInfo.setStr2( string2 );
            editStepInfo.setSed( string1.length() );
            return editStepInfo;
        }
        int len1 = string1.length();
        int len2 = string2.length();
        for( int i=0;i<len1;i++ ){
            String str1 = string1.substring(0, i + 1);
            for( int j=0;j<len2;j++ ){
                String str2 = string2.substring(0, j + 1);

                EditStepInfo editStepInfo = new EditStepInfo();
                editStepInfo.setStr1( str1 );
                editStepInfo.setStr2( str2 );
                if( str1.length() == 1 ){
                    if( str2.length() == 1 ){
                        if( str1.equals( str2 ) ){
                            // str1 和 str2 的长度均为1,并且 str1 和 str2 相等
                            // a
                            // a
                            List<StepVO> steps = new ArrayList<>();
                            editStepInfo.setSteps( steps );
                            editStepInfo.setTempString( str1 );
                            editStepInfo.setSed( steps.size() );
                        }else {
                            // str1 和 str2 的长度均为1,并且 str1 和 str2 不相等
                            // a
                            // b
                            List<StepVO> steps = new ArrayList<>();
                            StepVO step = new StepVO();
                            step.setDesc( "将 " + str1 + " 修改为 " + str2 );
                            step.setTempString( str2 );
                            step.setOptType( StepVO.OPT_TYPE_UPDATE );
                            step.setOptChar1( str1.charAt( 0 ) );
                            step.setOptIndex( 0 );
                            step.setOptChar2( str2.charAt( 0 ) );
                            steps.add( step );
                            editStepInfo.setSteps( steps );
                            editStepInfo.setTempString( getLastTempString( steps ) );
                            editStepInfo.setSed( steps.size() );
                        }
                    }else {
                        if( str2.contains( str1 ) ){
                            // str1 的长度为1,str2 的长度大于1,并且 str1 在 str2 中出现
                            //   a
                            // ..a...
                            //  组装编辑步骤信息
                            List<StepVO> steps = buildEditSteps(  str1.charAt(0),str2 );
                            editStepInfo.setSteps( steps );
                            editStepInfo.setTempString( getLastTempString( steps ) );
                            editStepInfo.setSed( steps.size() );
                        }else {
                            // str1 的长度为1,str2的长度大于1,并且str1在 str2中不存在
                            //   a
                            // ..b...
                            //  组装编辑步骤信息
                            List<StepVO> steps = buildEditSteps(str1.charAt(0), str2);
                            editStepInfo.setSteps( steps );
                            editStepInfo.setTempString( getLastTempString( steps ) );
                            editStepInfo.setSed( steps.size() );
                        }
                    }
                }else {
                    if( str2.length() == 1 ){
                        if( str1.contains( str2 ) ){
                            // ...a..
                            //    a
                            //  组装编辑步骤信息
                            List<StepVO> steps = buildEditSteps(str1, str2.charAt(0));
                            editStepInfo.setSteps( steps );
                            editStepInfo.setTempString( getLastTempString( steps ) );
                            editStepInfo.setSed( steps.size() );
                        }else {
                            // ...b..
                            //    a
                            //  组装编辑步骤信息
                            List<StepVO> steps = buildEditSteps(str1, str2.charAt(0));
                            editStepInfo.setSteps( steps );
                            editStepInfo.setTempString( getLastTempString( steps ) );
                            editStepInfo.setSed( steps.size() );
                        }
                    }else {
                        Character lastChar1 = getLastChar(str1);
                        Character lastChar2 = getLastChar(str2);
                        if( lastChar1.equals( lastChar2 ) ){
                            //    ------a
                            // ---------a
                            //  组装编辑步骤信息
                            EditStepInfo editStepInfo_prev = MAT_SED[i - 1][j - 1];
                            List<StepVO> steps = new ArrayList<>();
                            List<StepVO> steps_prev = editStepInfo_prev.getSteps();
                            String tempString = editStepInfo_prev.getTempString() + lastChar1;
                            if( steps_prev != null ){
                                steps.addAll( steps_prev );
                            }
                            editStepInfo.setSteps( steps );
                            editStepInfo.setTempString( tempString );
                            editStepInfo.setSed( steps.size() );
                        }else {
                            //    ----- a
                            // ........ b
                            // 1. str1 的 "-----" 部分转换为 str2,再删除 a
                            // 2. str1 转换为 str2 的 "........" 部分,再添加 b
                            // 3. str1 的 "-----" 部分转换为 str2 的 "........" 部分,再将a修改为 b
                            // 求 方法1、2、3中选一个最小的编辑步骤作为最终的编辑步骤
                            EditStepInfo editStepInfo_prev_1 = MAT_SED[i - 1][j];
                            EditStepInfo editStepInfo_prev_2 = MAT_SED[i][j - 1];
                            EditStepInfo editStepInfo_prev_3 = MAT_SED[i - 1][j - 1];
                            EditStepInfo editStepInfo_prev_min = editStepInfo_prev_1;
                            int minMethodNum = 1;
                            if( editStepInfo_prev_2.getSed() < editStepInfo_prev_min.getSed() ){
                                editStepInfo_prev_min = editStepInfo_prev_2;
                                minMethodNum = 2;
                            }
                            if( editStepInfo_prev_3.getSed() < editStepInfo_prev_min.getSed() ){
                                editStepInfo_prev_min = editStepInfo_prev_3;
                                minMethodNum = 3;
                            }
                            List<StepVO> steps = new ArrayList<>();
                            List<StepVO> steps_prev_min = editStepInfo_prev_min.getSteps();
                            if( steps_prev_min != null ){
                                steps.addAll( steps_prev_min );
                            }
                            StepVO step_last = null;
                            if( steps.size() > 0 ){
                                step_last = steps.get(steps.size() - 1);
                            }
                            String tempString = editStepInfo_prev_min.getTempString();
                            StepVO step = new StepVO();
                            if( minMethodNum == 1 ){
                                step.setDesc( "删除 " + lastChar1 );
                                step.setOptType( StepVO.OPT_TYPE_DELETE );
                                step.setOptChar1( lastChar1 );
                                step.setOptIndex( str1.length() - 1 );
                                // todo 估计有问题???
                                step.setTempString( tempString );
                            }else if( minMethodNum == 2 ){
                                step.setDesc( "添加 " + lastChar2 );
                                step.setOptType( StepVO.OPT_TYPE_ADD );
                                step.setOptChar1( lastChar2 );
                                if( step_last != null ){
                                    step.setOptIndex( step_last.getOptIndex() + 1 );
                                }else {
                                    step.setOptIndex( str1.length() );
                                }
                                // todo 估计有问题???
                                step.setTempString( tempString + lastChar2 );
                            }else if( minMethodNum == 3 ){
                                step.setDesc( "修改 " + lastChar1 + " 为 " + lastChar2 );
                                step.setOptType( StepVO.OPT_TYPE_UPDATE );
                                step.setOptChar1( lastChar1 );
                                step.setOptChar2( lastChar2 );
                                step.setOptIndex( str1.length() - 1 );
                                // todo 估计有问题???
                                step.setTempString( tempString + lastChar2 );
                            }
                            steps.add( step );
                            editStepInfo.setSteps( steps );
                            editStepInfo.setTempString( tempString );
                            editStepInfo.setSed( steps.size() );
                        }
                    }
                }
                MAT_SED[ i ][ j ] = editStepInfo;
            }
        }
        return MAT_SED[ string1.length() - 1 ][ string2.length() - 1 ];
    }

    private static String getLastTempString(List<StepVO> steps) {
        if( steps == null || steps.size() == 0 ){
            return null;
        }
        return steps.get( steps.size() - 1 ).getTempString();
    }

    /**
     * 组装将字符 srcChar 转换成字符串 targetString 的编辑步骤
     * @param srcChar 例如:a
     * @param targetString 例如:bcdefg
     * @return
     */
    private static List<StepVO> buildEditSteps(Character srcChar, String targetString) {
        boolean hasMeet = false;
        int length = targetString.length();
        List<StepVO> steps = new ArrayList<>();
        for( int i = 0;i < length;i++ ){
            Character char2 = targetString.charAt( i );
            if( hasMeet ){
                StepVO step = new StepVO();
                step.setDesc( "添加 " + char2 );
                step.setOptChar1( char2 );
                step.setOptIndex( i );
                step.setOptType( StepVO.OPT_TYPE_ADD );
                steps.add( step );
            }else {
                if( srcChar.equals( char2 ) ){
                    // do nothing
                    hasMeet = true;
                }else {
                    StepVO step = new StepVO();
                    step.setDesc( "添加 " + char2 );
                    step.setOptChar1( char2 );
                    step.setOptIndex( i );
                    step.setOptType( StepVO.OPT_TYPE_ADD );
                    steps.add( step );
                }
            }
        }
        if( !hasMeet ){
            // 此种情况只发生在 targetString 中不包含 srcChar 时
            StepVO step = new StepVO();
            step.setDesc( "删除 " + srcChar );
            step.setOptChar1( srcChar );
            step.setOptIndex( 0 );
            step.setOptType( StepVO.OPT_TYPE_DELETE );
            steps.add( 0,step );
        }
        //  设置 每个步骤生成的  tempString
        String tempString = String.valueOf( srcChar );
        for( StepVO step:steps ){
            String optType = step.getOptType();
            Character optChar1 = step.getOptChar1();
            if( StepVO.OPT_TYPE_DELETE.equals( optType ) ){
                tempString = tempString.replaceFirst( optChar1.toString(),"" );
            }else if( StepVO.OPT_TYPE_ADD.equals( optType ) ){
                tempString += optChar1;
            }
            step.setTempString( tempString );
        }
        return steps;
    }

    private static List<StepVO> buildEditSteps(String srcString, Character targetChar) {
        // abcdefg
        // c
        boolean hasMeet = false;
        int length = srcString.length();
        List<StepVO> steps = new ArrayList<>();
        for( int i = 0;i < length;i++ ){
            Character char1 = srcString.charAt( i );
            if( hasMeet ){
                StepVO step = new StepVO();
                step.setDesc( "删除 " + char1 );
                step.setOptChar1( char1 );
                step.setOptIndex( i );
                step.setOptType( StepVO.OPT_TYPE_DELETE );
                steps.add( step );
            }else {
                if( targetChar.equals( char1 ) ){
                    // do nothing
                    hasMeet  =true;
                }else {
                    StepVO step = new StepVO();
                    step.setDesc( "删除 " + char1 );
                    step.setOptChar1( char1 );
                    step.setOptIndex( i );
                    step.setOptType( StepVO.OPT_TYPE_DELETE );
                    steps.add( step );
                }
            }
        }
        if( !hasMeet ){
            StepVO step = new StepVO();
            step.setDesc( "添加 " + targetChar );
            step.setOptChar1( targetChar );
            step.setOptIndex( 0 );
            step.setOptType( StepVO.OPT_TYPE_ADD );
            steps.add( 0,step );
        }
        String tempString = srcString;
        for( StepVO step:steps ){
            String optType = step.getOptType();
            Character optChar1 = step.getOptChar1();
            if( StepVO.OPT_TYPE_ADD.equals( optType ) ){
                tempString += optChar1;
            }else if( StepVO.OPT_TYPE_DELETE.equals( optType ) ){
                tempString = tempString.replaceFirst( optChar1.toString(),"" );
            }
            step.setTempString( tempString );
        }
        return steps;
    }


    private static Character getLastChar(String str) {
        if( str == null || str.length() == 0 ){
            return null;
        }
        return  str.charAt(str.length() - 1);
    }
}
mother
monster
最小编辑距离:3
编辑步骤:
添加 n
添加 s
删除 h
11-11 09:53