Eugene W. Myers 在他1986年发表于"Algorithmica"的论文"An O(ND) Difference Algorithm and Its Variations"中描述了一种用于处理diff的基础贪婪算法. 在他的论文中, 还对这种算法进行了扩展"Linear Space Refinement".

定义文件A和文件B, 算法会读取两个文件的输入, 假设B为新版本, 算法会生成一段Shortest Edit Script (SES, 最短编辑脚本) 用于将A转换为B . SES只包含两种命令: 从A中删除, 以及在B中插入
寻找SES 等价于寻找 Longest Common Subsequence ( LCS 最长公共子序列 ), LCS是两个文件中去掉一些字符后, 所产生的共有的最长的字符串序列. 注意, 这与 Longest Common Substring (最长公共字符串)不同, 后者是必须连续的.

两个文件中, 可能存在多个LCS, 例如ABC和ACB, 存在两个LCS "AB"和"AC", 在这里分别对应了一个SES. 这个算法在存在多个SES时, 仅返回第一个找到的SES.

算法的运作是依赖于A和B文件构成的有向编辑图, 图中A为X轴, B为Y轴, 假定A和B的长度分别为m, n, 每个坐标代表了各自字符串中的一个字符. 在图中沿X轴前进代表删除A中的字符, 沿Y轴前进代表插入B中的字符. 在横坐标于纵坐标字符相同的地方, 会有一条对角线连接左上与右下两点, 表示不需任何编辑, 等价于路径长度为0. 算法的目标, 就是寻找到一个从坐标(0, 0)到(m, n)的最短路径

算法在比较中, 定义了以下变量

k: 左上至右下的对角线, 以(0,0)对应的对角线k=0, 左侧为-1, -2, ... 右侧为1, 2, ...
d: 路径长度
x, y: 坐标

snake: 代表了一步操作及其后面跟随的对角线移动

Source: https://www.codeproject.com/Articles/42279/Investigating-Myers-diff-algorithm-Part-2-of-2

Java代码

public static void main(String[] args) {
String a = "ABCABBACDAB";
String b = "CBABACDAA";
char[] aa = a.toCharArray();
char[] bb = b.toCharArray();
int max = aa.length + bb.length;
int[] v = new int[max * 2];
List<Snake> snakes = new ArrayList<>(); for (int d = 0; d <= aa.length + bb.length; d++) {
System.out.println("D:" + d);
for (int k = -d; k <= d; k += 2) {
System.out.print("k:" + k);
// down or right?
boolean down = (k == -d || (k != d && v[k - 1 + max] < v[k + 1 + max]));
int kPrev = down ? k + 1 : k - 1; // start point
int xStart = v[kPrev + max];
int yStart = xStart - kPrev; // mid point
int xMid = down ? xStart : xStart + 1;
int yMid = xMid - k; // end point
int xEnd = xMid;
int yEnd = yMid; // follow diagonal
int snake = 0;
while (xEnd < aa.length && yEnd < bb.length && aa[xEnd] == bb[yEnd]) {
xEnd++;
yEnd++;
snake++;
}
// save end point
v[k + max] = xEnd;
// record a snake
snakes.add(0, new Snake(xStart, yStart, xEnd, yEnd));
System.out.print(", start:("+xStart+","+yStart+"), mid:("+xMid+","+yMid+"), end:("+xEnd+","+yEnd + ")\n");
// check for solution
if (xEnd >= aa.length && yEnd >= bb.length) {
/* solution has been found */
System.out.println("found");
/* print the snakes */
Snake current = snakes.get(0);
System.out.println(String.format("(%2d, %2d)<-(%2d, %2d)", current.getxEnd(), current.getyEnd(), current.getxStart(), current.getyStart()));
for (int i = 1; i < snakes.size(); i++) {
Snake tmp = snakes.get(i);
if (tmp.getxEnd() == current.getxStart()
&& tmp.getyEnd() == current.getyStart()) {
current = tmp;
System.out.println(String.format("(%2d, %2d)<-(%2d, %2d)", current.getxEnd(), current.getyEnd(), current.getxStart(), current.getyStart()));
if (current.getxStart() == 0 && current.getyStart() == 0) {
break;
}
}
}
return;
}
}
}
} public static class Snake {
private int xStart;
private int yStart;
private int xEnd;
private int yEnd; public Snake(int xStart, int yStart, int xEnd, int yEnd) {
this.xStart = xStart;
this.yStart = yStart;
this.xEnd = xEnd;
this.yEnd = yEnd;
} public int getxStart() {
return xStart;
} public void setxStart(int xStart) {
this.xStart = xStart;
} public int getyStart() {
return yStart;
} public void setyStart(int yStart) {
this.yStart = yStart;
} public int getxEnd() {
return xEnd;
} public void setxEnd(int xEnd) {
this.xEnd = xEnd;
} public int getyEnd() {
return yEnd;
} public void setyEnd(int yEnd) {
this.yEnd = yEnd;
}
}

运行结果

D:0
k:0, start:(0,-1), mid:(0,0), end:(0,0)
D:1
k:-1, start:(0,0), mid:(0,1), end:(0,1)
k:1, start:(0,0), mid:(1,0), end:(1,0)
D:2
k:-2, start:(0,1), mid:(0,2), end:(2,4)
k:0, start:(1,0), mid:(1,1), end:(2,2)
k:2, start:(1,0), mid:(2,0), end:(3,1)
D:3
k:-3, start:(2,4), mid:(2,5), end:(3,6)
k:-1, start:(2,4), mid:(3,4), end:(4,5)
k:1, start:(3,1), mid:(3,2), end:(5,4)
k:3, start:(3,1), mid:(4,1), end:(5,2)
D:4
k:-4, start:(3,6), mid:(3,7), end:(4,8)
k:-2, start:(4,5), mid:(4,6), end:(4,6)
k:0, start:(5,4), mid:(5,5), end:(5,5)
k:2, start:(5,4), mid:(6,4), end:(10,8)
k:4, start:(5,2), mid:(6,2), end:(7,3)
D:5
k:-5, start:(4,8), mid:(4,9), end:(4,9)
k:-3, start:(4,8), mid:(5,8), end:(5,8)
k:-1, start:(5,5), mid:(5,6), end:(5,6)
k:1, start:(10,8), mid:(10,9), end:(10,9)
k:3, start:(10,8), mid:(11,8), end:(11,8)
k:5, start:(7,3), mid:(8,3), end:(8,3)
D:6
k:-6, start:(4,9), mid:(4,10), end:(4,10)
k:-4, start:(5,8), mid:(5,9), end:(5,9)
k:-2, start:(5,8), mid:(6,8), end:(7,9)
k:0, start:(10,9), mid:(10,10), end:(10,10)
k:2, start:(11,8), mid:(11,9), end:(11,9)
found
(11, 9)<-(11, 8)
(11, 8)<-(10, 8)
(10, 8)<-( 5, 4)
( 5, 4)<-( 3, 1)
( 3, 1)<-( 1, 0)
( 1, 0)<-( 0, 0)

动态规划处理diff算法 Myers Diff (正向)的更多相关文章

  1. 文本diff算法Patience Diff

    一般在使用 Myers diff算法及其变体时, 对于下面这种例子工作不是很好, 让变化不易阅读, 并且容易导致合并冲突 void Chunk_copy(Chunk *src, size_t src_ ...

  2. React Diff算法一览

    前言 diff算法一直是React系统最核心的部分,并且由于演化自传统diff,使得比较方式从O(n^3)降级到O(n),然后又改成了链表方式,可谓是变化万千. 传统Diff算法 传统diff算法需要 ...

  3. 【React自制全家桶】二、分析React的虚拟DOM和Diff算法

    一.React如何更新DOM内容: 1.  获取state 数据 2.  获取JSX模版 3.  通过数据 +模版结合,生成真实的DOM, 来显示,以下行代码为例(简称代码1) <div id= ...

  4. 深入理解React:diff 算法

    目录 序言 React 的核心思想 传统 diff 算法 React diff 两个假设 三个策略 diff 具体优化 tree diff component diff element diff 小结 ...

  5. 虚拟DOM与diff算法

    虚拟DOM与diff算法 虚拟DOM 在DOM操作中哪怕我们的数据,发生了一丢丢的变化,也会被强制重建整预DOM树.这么做,涉及到很多元素的重绘和重排,导致性能浪费严重 只要实现按需更新页面上的元素即 ...

  6. 探究虚拟dom与diff算法

    一.虚拟DOM (1)什么是虚拟DOM? vdom可以看作是一个使用javascript模拟了DOM结构的树形结构,这个树结构包含整个DOM结构的信息,如下图:   可见左边的DOM结构,不论是标签名 ...

  7. diff算法深入一下?

    文章转自豆皮范儿-diff算法深入一下 一.前言 有同学问:能否详细说一下 diff 算法. 简单说:diff 算法是一种优化手段,将前后两个模块进行差异化比较,修补(更新)差异的过程叫做 patch ...

  8. react diff算法浅析

    diff算法作为Virtual DOM的加速器,其算法的改进优化是React整个界面渲染的基础和性能的保障,同时也是React源码中最神秘的,最不可思议的部分 1.传统diff算法计算一棵树形结构转换 ...

  9. 深入理解react中的虚拟DOM、diff算法

    文章结构: React中的虚拟DOM是什么? 虚拟DOM的简单实现(diff算法) 虚拟DOM的内部工作原理 React中的虚拟DOM与Vue中的虚拟DOM比较 React中的虚拟DOM是什么?   ...

随机推荐

  1. Java学习笔记——IO操作之以图片地址下载图片

    以图片地址下载图片 读取给定图片文件的内容,用FileInputStream public static byte[] mReaderPicture(String filePath) { byte[] ...

  2. 附9 elasticsearch-curator + Linux定时任务

    官网教程入口:https://www.elastic.co/guide/en/elasticsearch/client/curator/current/index.html 一.下载安装 下载:sud ...

  3. LeetCode295-Find Median from Data Stream && 480. 滑动窗口中位数

    中位数是有序列表中间的数.如果列表长度是偶数,中位数则是中间两个数的平均值. 例如, [2,3,4] 的中位数是 3 [2,3] 的中位数是 (2 + 3) / 2 = 2.5 设计一个支持以下两种操 ...

  4. Entity Framework 与 LINQ to SQL

    Entity Framework和LINQ to SQL到底有什么区别?这是一个很常见的问题.下面的表中简要罗列了两种技术的主要区别. LINQ to SQL Entity Framework 复杂度 ...

  5. iOS开发-UIActionSheet简单介绍

    UIActionSheet和UIAlertView都是ios系统自带的模态视图,模态视图的一个重要的特性就是在显示模态视图的时候可以阻断其他视图的事件响应.一般情况下我们对UIAlertView使用的 ...

  6. Spring Boot 直接用jar运行项目

    概述 在Spring Boot 开篇-创建和运行一文中,介绍了如何创建一个Sprint Boot项目并且运行起来.但是运行的方式是在IDEA中直接Run起来的.还有另一中方式可以可以把Spring B ...

  7. oracle的行级触发器使用

    行级触发器: 当触发器被触发时,要使用被插入.更新或删除的记录中的列值,有时要使用操作前.后列的值. :NEW 修饰符访问操作完成后列的值 :OLD 修饰符访问操作完成前列的值 例1: 建立一个触发器 ...

  8. 【Spark】Sparkstreaming-共享变量-缓存RDD-到底是什么情况?

    Sparkstreaming-共享变量-缓存RDD-到底是什么情况? sparkstreaming 多个 rdd_百度搜索 Spark Streaming中空RDD处理及流处理程序优雅的停止 - xu ...

  9. 【Scala】Scala学习资料

    Scala学习资料 java 树形 分类器_百度搜索 决策树分类器-Java实现 - CSDN博客 KNN分类器-Java实现 - CSDN博客 学习:java设计模式—分类 - 飞翔荷兰人 - 博客 ...

  10. Mahout 安装配置

    http://log.medcl.net/item/2011/02/mahout_install/ Apache Mahout是一个机器学习的框架,构建在hadoop上支持大规模数据集的处理,目前最新 ...