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

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

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
动态规划是计算最短编辑距离的一种常用方法。根据给定的字符串,我们可以通过构建一个二维数组来表示最短编辑距离。假设字符串str1的长度为m,字符串str2的长度为n,我们可以定义一个大小为(m+1)×(n+1)的二维数组dp。 动态规划的推导过程如下: 1. 初始化dp数组,dp[i]表示将str1的前i个字符转换为空字符串的最小编辑距离,dp[j]表示将空字符串转换为str2的前j个字符的最小编辑距离。因此,dp[i] = i,dp[j] = j。 2. 遍历数组dp,计算dp[i][j]的值。 - 如果str1的第i个字符等于str2的第j个字符(即str1[i-1] == str2[j-1]),则dp[i][j] = dp[i-1][j-1],表示不需要进行编辑操作。 - 否则,dp[i][j]的值可以通过以下三种操作得到: - 如果将str1的前i-1个字符转换为str2的前j个字符的最小编辑距离为dp[i-1][j],则将str1的第i个字符删除,得到str1的前i-1个字符和str2的前j个字符的最小编辑距离为dp[i][j] = dp[i-1][j] + 1。 - 如果将str1的前i个字符转换为str2的前j-1个字符的最小编辑距离为dp[i][j-1],则将str2的第j个字符插入到str1的第i+1个位置,得到str1的前i个字符和str2的前j个字符的最小编辑距离为dp[i][j] = dp[i][j-1] + 1。 - 如果将str1的前i-1个字符转换为str2的前j-1个字符的最小编辑距离为dp[i-1][j-1],则将str1的第i个字符替换为str2的第j个字符,得到str1的前i个字符和str2的前j个字符的最小编辑距离为dp[i][j] = dp[i-1][j-1] + 1。 - 取上述三种操作中的最小值作为dp[i][j]的值。 3. 最终,最小编辑距离为dp[m][n]。 在本例中,示例输入为str1="ABC",str2="DCB",则根据动态规划的推导过程,可以计算得到最小编辑距离为3。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值