使用求2个字符串最短编辑距离动态规划算法实现 git diff 算法 java 实现

测试类 MyDiffTest.java:
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;


public class MyDiffTest {

   
     private static String path = "\\xxx\\";

    private static List<String> lines_v1 = readFile2List( path + "DemoClass1.java" );
    private static List<String> lines_v2 = readFile2List( path + "DemoClass2.java" );

    public static void main(String[] args) {
        /*lines_v1 = new ArrayList<>();
        lines_v2 = new ArrayList<>();
        lines_v1.add( "m" );
        lines_v1.add( "o" );
        lines_v1.add( "t" );
        lines_v1.add( "h" );
        lines_v1.add( "e" );
        lines_v1.add( "r" );

        lines_v2.add( "m" );
        lines_v2.add( "o" );
        lines_v2.add( "n" );
        lines_v2.add( "s" );
        lines_v2.add( "t" );
        lines_v2.add( "e" );
        lines_v2.add( "r" );*/

        int[][] dp =  calculateMinimumTransferWay(lines_v1, lines_v2);

        int index1 = lines_v1.size() - 1;
        int index2 = lines_v2.size() - 1;
        System.out.println( "最短编辑距离:" +  dp[index1][index2] );

        List<String> result = new ArrayList<>();
        while ( index1 >= 0 && index2 >= 0 ){
            String line_v1 = lines_v1.get(index1);
            String line_v2 = lines_v2.get(index2);

            if( line_v1.equals( line_v2 ) ){
                // v1:...a
                // v2:...a
                // 此时,v1 和 v2 的当前行相同,原封不懂的输出,表示没有改动
                result.add( "  " + line_v1 );
                index1--;
                index2--;
            }else {
                // v1:...a
                // v2:...b
                // 此时,v1版本的当前行和 v2版本的当前行不相同,所以v1转化为v2过程中,对 行a、行b的操作有3种可能:新增行b、删除行a

                // v1:...a
                // v2: ...  b
                // 如果此时的编辑距离是最小的,则v2版本需要新增 行b
                int sed1 = dp[index1][index2 - 1] + 1; // ps:sed 是 shortest edit distance 的缩写

                // v1: ...  a
                // v2:...b
                // 如果此时的编辑距离是最小的,则v2版本需要删除 行a
                int sed2 = dp[ index1 - 1 ][ index2 ] + 1;

                // v1: ...  a
                // v2: ...  b
                // 如果此时的编辑路基是最小的,则v2版本需要将 删除行a+新增行b( 一共2步操作 )
                int sed3 = dp[ index1 - 1 ][ index2 - 1 ] + 2;

                int sed = Math.min(Math.min(sed1, sed2), sed3);
                if( sed1 == sed ){
                    result.add( "+ " + line_v2 );
                    index2--;
                }else if( sed2 == sed ){
                    result.add( "- " + line_v1 );
                    index1--;
                }else if( sed3 == sed ){
                    result.add( "+ " + line_v2 );
                    result.add( "- " + line_v1 );
                    index1--;
                    index2--;
                }
            }
        }
        while ( index1 >= 0 ){
            // v1 还剩下的一些 "首行们" 都是需要被删除的行,因为v2版本没有
            result.add( "- " + lines_v1.get( index1 ) );
            index1--;
        }

        while ( index2 >= 0 ){
            // v2 还剩下的一些 "首行们" 都是需要被添加的行,因为v1版本没有
            result.add( "+ " + lines_v2.get( index2 ) );
            index2--;
        }

        for (int i = ( result.size() - 1 ); i >= 0;i-- ) {
            System.out.println( result.get( i ) );
        }
    }

    private static List<String> readFile2List( String filePath ){
        BufferedReader reader = null;
        try {
            List<String> lines = new ArrayList<>();
            reader = new BufferedReader(new FileReader(filePath));
            String line = reader.readLine();
            while (line != null) {
                lines.add( line );
                line = reader.readLine();
            }
            return lines;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static int[][] calculateMinimumTransferWay(List<String> lines_v1, List<String> lines_v2 ){
        int size_v1 = lines_v1.size();
        int size_v2 = lines_v2.size();
        int[][] dp = new int[ size_v1 ][ size_v2 ];

        for (int index1 = 0; index1 < size_v1; index1++) {
            String line_v1 = lines_v1.get( index1 );
            for (int index2 = 0; index2 < size_v2; index2++) {
                String line_v2 = lines_v2.get( index2 );
                if( index1 == 0 ){
                    if( index2 == 0 ){
                        if( line_v1.equals( line_v2 ) ){
                            // v1:a
                            // v2:a
                            // 无需任何操作
                            dp[ index1 ][ index2 ] = 0;
                        }else {
                            // 不相同,需要 1 步删除操作,1步插入操作
                            // v1:a
                            // v2:b
                            dp[ index1 ][ index2 ] = 2;
                        }
                    }else {
                        if( getFirstEqualIndex( lines_v2,line_v1,0,index2 ) != -1 ){
                            // v1:      a
                            // v2:...a...
                            // 需要 index2 步插入操作
                            dp[ index1 ][ index2 ] = index2;
                        }else {
                            // v1:      a
                            // v2:...b...
                            // 需要1步删除操作,index2 + 1步插入操作
                            dp[ index1 ][ index2 ] = index2 + 2;
                        }
                    }
                }else {
                    if( index2 == 0 ){
                        if( getFirstEqualIndex(lines_v1, line_v2, 0, index1) != -1 ){
                            // v1:...a...
                            // v2:      a
                            // 需要 index1 步删除操作
                            dp[ index1 ][ index2 ] = index1;
                        }else {
                            // v1:...a...
                            // v2:      b
                            // 需要 index1 + 1 步删除操作,1步插入操作
                            dp[ index1 ][ index2 ] = index1 + 2;
                        }
                    }else {
                        if( line_v1.equals( line_v2 ) ){
                            // v1:...a
                            // v2:...a
                            dp[ index1 ][ index2 ] = dp[index1 - 1][index2 - 1];
                        }else {
                            // v1:...a
                            // v2:...b
                            // sed means "shorest edit distance"
                            int sed1 = dp[index1 - 1][index2];
                            int sed2 = dp[index1 ][index2 - 1];
                            int sed3 = dp[index1 - 1][index2 - 1];
                            int sed = Math.min( Math.min( sed1,sed2 ),sed3 );
                            if( sed1 == sed || sed2 == sed ){
                                dp[ index1 ][ index2 ] = sed + 1;
                            }else {
                                dp[ index1 ][ index2 ] = sed + 2;
                            }
                        }
                    }
                }
            }
        }
        return dp;
    }

    private static int getFirstEqualIndex(List<String> lines, String targetLine, int beginIndex, int endIndex) {
        for (int i = beginIndex; i <=endIndex ; i++) {
            if( targetLine.equals( lines.get( i ) ) ){
                return i;
            }
        }
        return -1;
    }
}

旧版本文本文件 DemoClass1.java:

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.text.similarity.LevenshteinDistance;

@Slf4j
public class DemoClass1 {

    private static final LevenshteinDistance LEVENSHTEIN_DISTANCE = LevenshteinDistance.getDefaultInstance();

    public static String null2emptyWithTrim( String str ){
        if( str == null ){
            str = "";
        }
        str = str.trim();
        return str;
    }

    public static String requiredStringParamCheck(String param, String paramRemark) {
        param = null2emptyWithTrim( param );
        if( param.length() == 0 ){
            String msg = "操作失败,请求参数 \"" + paramRemark + "\" 为空";
            log.error( msg );
            throw new BusinessLogicException( msg );
        }
        return param;
    }

    public static double calculateSimilarity( String str1,String str2 ){
        int distance = LEVENSHTEIN_DISTANCE.apply(str1, str2);
        double similarity = 1 - (double) distance / Math.max(str1.length(), str2.length());
        System.out.println("相似度:" + similarity);
        return similarity;
    }
}

新版本文本文件 DemoClass2.java:

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.text.similarity.LevenshteinDistance;

@Slf4j
public class DemoClass2 {

    private static final LevenshteinDistance LEVENSHTEIN_DISTANCE = LevenshteinDistance.getDefaultInstance();
    private static final LevenshteinDistance LEVENSHTEIN_DISTANCE1 = LevenshteinDistance.getDefaultInstance();
    private static final LevenshteinDistance LEVENSHTEIN_DISTANCE2 = LevenshteinDistance.getDefaultInstance();

    public static String null2emptyWithTrim( String str ){
        // if( str == null ){
        //     str = "";
        // }
        // str = str.trim();
        return str;
    }

    public static String requiredStringParamCheck(String param, String paramRemark) {
        return null;
    }

    public static double calculateSimilarity( String str1,String str2 ){
        try {
            int distance = LEVENSHTEIN_DISTANCE.apply(str1, str2);
            double similarity = 1 - (double) distance / Math.max(str1.length(), str2.length());
            return similarity;
        }catch ( Exception e ){
            e.printStackTrace();
            return 0d;
        }
    }
}

测试输出:


  import com.goldwind.ipark.common.exception.BusinessLogicException;
  import lombok.extern.slf4j.Slf4j;
  import org.apache.commons.text.similarity.LevenshteinDistance;
  
  @Slf4j
- public class DemoClass1 {
+ public class DemoClass2 {
  
      private static final LevenshteinDistance LEVENSHTEIN_DISTANCE = LevenshteinDistance.getDefaultInstance();
+     private static final LevenshteinDistance LEVENSHTEIN_DISTANCE1 = LevenshteinDistance.getDefaultInstance();
+     private static final LevenshteinDistance LEVENSHTEIN_DISTANCE2 = LevenshteinDistance.getDefaultInstance();
  
      public static String null2emptyWithTrim( String str ){
-         if( str == null ){
-             str = "";
-         }
-         str = str.trim();
+         // if( str == null ){
+         //     str = "";
+         // }
+         // str = str.trim();
          return str;
      }
  
      public static String requiredStringParamCheck(String param, String paramRemark) {
-         param = null2emptyWithTrim( param );
-         if( param.length() == 0 ){
-             String msg = "操作失败,请求参数 \"" + paramRemark + "\" 为空";
-             log.error( msg );
-             throw new BusinessLogicException( msg );
-         }
-         return param;
+         return null;
      }
  
      public static double calculateSimilarity( String str1,String str2 ){
-         int distance = LEVENSHTEIN_DISTANCE.apply(str1, str2);
-         double similarity = 1 - (double) distance / Math.max(str1.length(), str2.length());
-         System.out.println("相似度:" + similarity);
-         return similarity;
+         try {
+             int distance = LEVENSHTEIN_DISTANCE.apply(str1, str2);
+             double similarity = 1 - (double) distance / Math.max(str1.length(), str2.length());
+             return similarity;
+         }catch ( Exception e ){
+             e.printStackTrace();
+             return 0d;
+         }
      }
  }

求做小编辑距离的时候,我这里不允许编辑操作,通常的求最小编辑距离一共允许三种操作( 删除、新增、编辑 ),其中一个编辑操作和删除、新增操作的权重都算作一步,我这里不允许编辑操作,比如迫不得已必须用编辑操作时,例如将a 行变为b行,我们必须先删除,后增加,其实等效于允许编辑操作,但是编辑操作权重大一些,为什么这样规定呢?

如上所示,新版本相对于旧版本来说是连续的修改了多行,我们人眼比较习惯这种差异比较后的输出:

而不是这种输出( 虽然逻辑上也对 ):

如果允许编辑操作( 或者编辑操作的权重和删除、新增操作一样时 ),就可能会出现这种情况,整块整块的修改给当做每一行来一个编辑操作。如果不允许编辑操作( 其实等价于将编辑操作的权重设置为删除或者插入操作的2倍 ),那么当出现整块的修改时,差异化比较时,被当做多个单行的修改的概率就被降低了,因为那样的话编辑次数会更多,所以会优先的视为多行的删除( 块删除 )操作和多行的新增( 块新增 )。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/214093.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HBase整合Phoenix

文章目录 一、简介1、Phoenix定义2、Phoenix架构 二、安装Phoenix1、安装 三、Phoenix操作1、Phoenix 数据映射2、Phoenix Shell操作3、Phoenix JDBC操作3.1 胖客户端3.2 瘦客户端 四、Phoenix二级索引1、为什么需要二级索引2、全局索引&#xff08;global index&#xff09;3、…

基于SSM的网上手机销售系统

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…

数据结构:堆的实现思路

我们之前写过堆的实现代码&#xff1a;数据结构&#xff1a;堆的实现-CSDN博客 这篇文章我们了解一下堆到底是如何实现的 1.堆向下调整算法 现在我们给出一个数组&#xff0c;逻辑上看做一颗完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆 向下调…

小米秒享3--非小米电脑

小米妙享中心是小米最新推出的一款功能&#xff0c;能够为用户们提供更加舒适便利的操作体验。简单的说可以让你的笔记本和你的小米手机联动&#xff0c;比如你在手机的文档&#xff0c;连接小米共享后&#xff0c;可以通过电脑进行操作。 对于非小米电脑想要体验终版秒享AIOT…

自带灯效的气传导耳机,声音当然好听,哈氪聆光体验

现在市场上的蓝牙耳机种类繁多&#xff0c;入耳式的算是主流&#xff0c;但不太适合户外使用 &#xff0c;我平时出门健身、散步的时候&#xff0c;更喜欢用气传导耳机。气传导耳机通常采用挂耳式的设计&#xff0c;耳机不入耳&#xff0c;佩戴舒适度更好&#xff0c;而且稳定性…

viple模拟器使用(四):unity模拟器中实现两距离局部最优迷宫算法

名字解读 两距离&#xff1a;指的是左侧距离和右侧距离 局部最优&#xff1a;对当前状态来说最好的选择&#xff0c;至于整体能不能达到最优&#xff0c;是无法确定的。 从节点1到节点5&#xff0c;一共有3条路 第1条路线&#xff1a;1→2→4→5&#xff0c;对应的花销是&…

Linux dig指令的十三种用法

文章目录 dig指令有哪些作用dig 具体用法推荐阅读 dig指令有哪些作用 DIG命令(Domain Information Groper命令)是一个网络工具&#xff0c;具有基本的命令行接口&#xff0c;用于进行不同的DNS(域名系统)查询。您可以使用DIG命令: 诊断您的域名服务器。检查所有这些服务器或每…

OpenWrt作为旁路由(网关)配置

目录 背景前提条件环境操作步骤物理层连接设置与主路由同一网段禁用IPv6取消LAN接口桥接防火墙配置 背景 本文简介如何配置OpenWrt&#xff0c;使其作为旁路由&#xff08;网关&#xff09;运行。 旁路由大概有以下这几种工作方式&#xff1a; 主路由开DHCP&#xff0c;网关未…

行为型剩余的模式

1.中介者模式 package com.jmj.pattern.mediator;public abstract class Mediator {public abstract void constact(String message,Person person); }package com.jmj.pattern.mediator;public class MediatorStructure extends Mediator{private HouseOwner houseOwner;priva…

Linux:dockerfile编写搭建nginx练习(8)

dockerfile是创建镜像的一种&#xff0c;通过已有镜像的基础上再在上面部署一些别的。 在这个基础镜像上搭建&#xff0c;我这个是一个空的centos镜像 我这里用http的yum仓库存放了nginx和rpm包 创建dockerfile vim Dockerfile写入#设置基础镜像 FROM centos#维护该镜像的用户…

C++ string类(1)—初始化、容量操作、迭代器

目录 前言 一、string类 二、初始化 1、无参或带参 2、用字符串变量初始化 3、用字符串初始化 4、指定数量字符 三、容量操作 1、size 2、push_back 3、append​编辑 4、运算符 5、reserve 6、resize 四、迭代器 1、正向迭代器 2、反向迭代器 3、const迭代器…

强化学习简明教程

到目前为止&#xff0c;我们主要关注监督学习问题&#xff08;主要是分类&#xff09;。 在监督学习中&#xff0c;我们得到某种由输入/输出对组成的训练数据&#xff0c;目标是能够在学习模型后根据一些新输入来预测输出。 例如&#xff0c;我们之前研究过 MNIST 的卷积神经网…

64. 最小路径和(Leetcode)

文章目录 前言一、题目分析二、算法原理1.状态表示2.状态转移方程3.初始化4.填表顺序5.返回值是什么 三、代码实现总结 前言 在本文章中&#xff0c;我们将要详细介绍一下Leetcode6最小路径相关的内容 一、题目分析 二、算法原理 1.状态表示 列出dp表&#xff0c;dp[i][j]代…

wvp gb28181 pro 平台国标级连功能说明

国标28181不同平台之间支持两种连接方式&#xff0c;平级和上下级&#xff0c;WVP目前支持向上级级联。 测试环境 测试平台上级&#xff1a;192.168.10.209&#xff08;Alam centos8&#xff09; 测试平台下级&#xff1a;192.168.10.206&#xff08;ky10_x86&#xff09; 下级…

OSG编程指南:专栏内容介绍及目录

1、专栏介绍 OpenSceneGraph&#xff08;OSG&#xff09;场景图形系统是一个基于工业标准 OpenGL 的软件接口&#xff0c;它让程序员能够更加快速、便捷地创建高性能、跨平台的交互式图形程序。本专栏基于 OSG 3.6.5版本进行源码的编写及扩展&#xff0c;也通用于其他OSG版本的…

算法通关村第十三关-黄金挑战数论问题

计数质数 描述 : 给定整数 n &#xff0c;返回 所有小于非负整数 n 的质数的数量 。 题目 : LeetCode 204.计数质数 : 204. 计数质数 分析 : 解决这个题有一个有效的方法&#xff0c;叫埃氏筛 , 后来又产生了线性筛&#xff0c;奇数筛等改进的方法。 基本思想是如果 x是…

基于SSM的新闻网站浏览管理实现与设计

基于ssm的新闻网站浏览管理实现与设计 摘要&#xff1a;在大数据时代下&#xff0c;科技与技术日渐发达的时代&#xff0c;人们不再局限于只获取自己身边的信息&#xff0c;而是对全球信息获取量也日渐提高&#xff0c;网络正是打开这新世纪大门的钥匙。在传统方式下&#xff…

逸学java【初级菜鸟篇】12.网络通讯编程

hi&#xff0c;我是逸尘&#xff0c;一起学java吧 目标&#xff08;任务驱动&#xff09; 请练掌网络通讯的内容。 局域网和互联网 局域网英文&#xff1a;Local Area Network&#xff0c;缩写&#xff1a;LAN&#xff0c;是指一群通过一定形式连接起来的计算机&#xff0c;…

【并发编程】CopyOnWriteArrayList详解与原理

&#x1f4eb;作者简介&#xff1a;小明Java问道之路&#xff0c;2022年度博客之星全国TOP3&#xff0c;专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化&#xff0c;文章内容兼具广度、深度、大厂技术方案&#xff0c;对待技术喜欢推理加验证&#xff0c;就职于…

Python函数的基本使用(一)

Python函数的基本使用&#xff08;一&#xff09; 一、函数概述二、函数的定义2.1 函数的语法2.2 语法说明2.3 函数定义的方式2.4 总结 三、函数的调用3.1 函数调用语法3.2 语法说明3.3 函数调用 四、函数的参数4.1 参数的分类4.2 必需参数4.3 默认值参数4.4 关键字参数4.5 不定…
最新文章