文章开头先上demo,只需键入任意内容的两个字符串,页面上就能自动计算并呈现字符串之间的差分。html
demo地址:string-diff-demo.herokuapp.comgit
源码地址:github.com/lqt0223/str…github
动态规划(dynamic programming)是你们在算法学习中都会遇到的话题之一。我我的对于它的理解是:算法
能够被动态规划解决的问题,常见的有:数组
曾经,我本身在codewar等网站上作算法题时,不少次刷到"longest common sequence"或者相似的题目,也经过一些算法书了解到这类问题的一个比较易于理解的算法是动态规划。但我一直不太明白这类问题的实际应用何在。直到最近看到了下面的论文:浏览器
An O(ND) Difference Algorithm and Its Variations - EUGENE W. MYERS数据结构
此文我只读了其中的1-2节,总结一下它的内容实际上是:使用图算法,求解两个字符串之间的LCS,以及最短编辑步骤(shortest edit script,如下简称SES,指的是从字符串A变换至字符串B,所须要的步骤。步骤是针对字符串的操做,例如删除某一位置上的字符、在某一位置上插入字符等)。app
今后文可知:LCS和SES是对偶问题(dual problem),这两个问题只不过是一个优化问题的两个方面。即,当咱们寻找两个字符串的公共子序列时,若是已经找到了最优解(最长公共子序列),那么在此最优解情形下的两个字符串之间的编辑步骤,也就是最短编辑步骤。通俗地讲,求解LCS的过程当中,咱们就能够获得SES。工具
因为SES描述了从一个字符串到另外一个字符串的一系列操做步骤,这就相似于各种数据比较工具产生的差量数据。因而咱们知道了,LCS问题的实际应用之一,就是数据的比较、差量计算和差量更新。学习
因为是用动态规划来求解LCS和SES问题,咱们须要用到矩阵(二维数组)来记录最优解的一些信息。
这一小节主要是说明在使用矩阵求解以上问题的过程当中,矩阵有哪些性质,以及这些性质对应着LCS或SES问题的什么方面。这些内容也是对于上一小节中提到的论文第2节内容的概括和简化。
若是以前没有接触过使用动态规划求解LCS问题的话,能够看一下下面的视频,从而对于这一求解过程有一个基本概念。
Longest Common Subsequence - Tushar Roy - Youtube
整体来讲,使用矩阵转化并求解LCS和SES问题须要如下三个阶段:
通过三个阶段后,矩阵会变成相似下图的形式。
图中是字符串A为"abcabba",字符串B为"cbabac"时,使用动态规划求解LCS和SES造成的矩阵。由此矩阵咱们能够得出如下关于两个字符串之间的LCS和SES的相关答案:
例:字符串A为"abcabba",字符串B为"cbabac"时,如何知道通过什么样的步骤,能够最快地将字符串A变为字符串B呢?咱们可使用上面的规则,将红色路径翻译成咱们须要的SES
- 删除字符串A的第一、2个字符(最左上角的两个向左箭头)
- 在字符串A的第3个位置添加字符"b"(从左上至右下的第四个向上箭头)
- 删除字符串A的第6个字符(从左上至右下的倒数第三个向左箭头)
- 在字符串A的第7个位置添加字符"c"(最右下角的向上箭头)
通过上述操做后咱们就能够将字符串A变换为字符串B
上一节的末尾给出了从"abcabba"到"cbabac"的SES,也许你试着用草稿纸或者其余工具来使用这段SES,但却没法顺利地完成字符串的转换。这是由于:SES所表示的编译步骤,须要被同时操做。这个说法比较抽象,下面使用"abcabba"到"cbabac"例子,说明SES的正确用法:
原字符串
a b c a b b a 复制代码
删除字符串A的第一、2个字符(最左上角的两个向左箭头)(这里用*标记将要被删除的字符)
* * c a b b a 复制代码
在字符串A的第3个位置添加字符"b"(从左上至右下的第四个向上箭头)
* * c a b b a b 复制代码
删除字符串A的第6个字符(从左上至右下的倒数第三个向左箭头)
* * c a b * a b 复制代码
在字符串A的第7个位置添加字符"c"(最右下角的向上箭头)
* * c a b * a b c 复制代码
将以上相似于hashTable的结构还原为一个字符串,规则为:遇到须要删除的字符时则忽略,遇到纵向伸展的list时将其连缀为一个子字符串,最后将全部子字符串按顺序链接,即获得"cbabac"
由此可知,SES的同时操做,指的是任何一个操做步骤,都不该该影响到字符串最初的字符排列。咱们能够用这种纵向的数据结构,从新整理字符串操做,并在最后转换成目标字符串。
如上一小节所示,SES的应用之一就是直接执行,其结果就是生成目标字符串。
咱们也能够结合原字符串和SES,生成DOM String,在浏览器中将原字符串到目标字符串的差分呈现出来。本文开头的demo便是对于这种应用方式的展现。
不只是字符级的diff & patch,若是在不考虑算法空间复杂度的状况下,动态规划也能够简单地实现单词级、行级的diff & patch。
学习和实现这个算法给我最大的体会是: