熟悉linux的朋友,对diff这个工具一定不会陌生。diff可以用来比对两份文件的异同。而在cvs svn这种版本控制系统中,diff更是发挥着重要作用 。
由于同一个项目有多个子版本,所以某一个子版本在进行了一些bug修复后,想把同样的修复应用到其它的版本上。使用cvs不知道是不是支持这种功能。所以,自己想写一个脚本,来将一些改动自动应用。
首先,想自己动手实现diff。
最初的想法是这样的:
1. 将2份文件的所有行读取到两个list中,src与tar,每个list设置2个游标,分别是Current 和 position,代表当前处理的行数以及在行与行不匹配的情况下,进行超前搜索。
2. 逐行进行比较
2.1 如果src.current == tar.current,表示该行没有改动,Current++
2.2 如果src.current != tar.current,表示两行不同
先对tar进行超前搜索,判断是否存在一行,与src.current相同
tar.position = tar.current+1
while tar.psotion<tar.size
if tar.postion == src.current
那么在tar.current 与tar.postion之间的行,就可以认为是tar文件比src文件多的行数
tar.current=tar.postion+1
src.current++
break
tar.postion++
如果在对tar文件搜索完全部行数后,仍未能找到一行满足tar.postion==src.current
那么就对src进行超前搜索,来判断是否存在src.postion = tar.current
while src.postion<src.size and tar.postion == tar.size <---表示tar进行了完全搜索,但为匹配
处理与对tar的处理一致
最后,如果对src进行了全部搜索,也没有发现匹配行,那么就说明src.current 与 tar.current 是对应行,但是有一行内容发生了变化
if src.postion == src.size
src.current 是内容变化的行,不是新加入的行
首先,这个算法肯定是有bug的。这是对文本差异进行搜索时的第一时间的想法,就是对文本逐行扫描。在现实测试中,这个算法还是能够处理大部分情况的,但是,还是有错误,比如,假如对下面的文本进行处理:
hello; hell;
you; you;
are; are;
great; hello;
great;
按照算法描述,最后会得出这样的结论,右侧的文本比左侧的文本多了hell;you;are;行,少了hello与great间的you;are;行。虽然,这样找出的也是两行文本的差异,但是,确切的说,这不是最好的答案,最起码,两个文件的差别没有那么大,仅仅是hello->hell和多了另一个hello。
所以,最开始想到的算法是不完备的,同时,也是混乱的,含着想当然的意味。
加入脱离现实业务本身,来对它进行一下抽象,我们会得到更好的解决方案。如果每一行只有一个字母的话,那么,就相当于寻找两个字符串的差异了。换句话说,要找出公共字符串的补集。然而,像 A-A^B 和 B-A^B这种集合运算,是不合适的,因为我们的 差异要涉及到顺序的概念。其实这就是lcs问题了。
所谓lcs,就是最长公共子串。子串的概念一定要清晰,就是对于一个串s0,s1...sn,保持顺序不变,从中抽取x个元素组成的新串。最长公共子串也就好理解了。有关概念问题,请参考最长公共子串。
重新回到文件上来,只要我们能够知道两份文件的最长公共内容,我们就很容易的可以得到两份文件存在的差异了。最长公共子串的实现算法,使用递归可以更好的进行描述。
对于两个串A和B,假如A和B的最后一个元素相同,那么,这个元素必然在公共子串中。去掉A与B的最后一个元素,再对新串的最后一个元素对比,相同即去掉,直到发现不相同的元素。对于不相同的元素,我们先去掉A的最后一个元素,让新串与B进行递归处理,得到A‘与B的最长子串,然后去掉B的最后元素,让A和B’递归处理,得到A和B‘的最长子串,取这两个子串中较长的一个,就是A、B在最后一个元素不同时的最长子串。很显然,递归的终止条件是A和B有一个的长度变为0
Python的实现如下:
def lcs2(seqx,seqy):
if len(seqx) == 0 or len(seqy)==0:
return []
if seqx[-1:] == seqy[-1:]:
result = lcs2(seqx[:-1],seqy[:-1])
result.append(seqx[-1:][0])
return result
else:
t1 = lcs2(seqx[:-1],seqy)
t2 = lcs2(seqx,seqy[:-1])
t = t1 if len(t1)>len(t2) else t2
return t
这个递归调用,显然存在着非常大的冗余。和计算Fibonacci数列一样,每一个元素都会被计算多次。所以, 原始算法的效率非常低,对于一个n-n类型的串比较,最坏要发生n*∑2^(i-1)*i 1<=i<=n 次比较,复杂度为O(n^3*2^n)。如果我们能够避免重复的对某一已知情况进行求解,就可以大大简化这个过程。采用动态规划算法时,需要一个n*m的表,来保存所有已知的解。比如,我们从A0与B0开始,表示两个串均为空串。那么A2与B串的公共串,其实就是在已知A1与B’的公共串的基础上,如果A2的最后一个元素与B最后一个相同个,那么就是A1与B‘的公共串再添加上最后A2最后一个,否则,就是A1与B的和A2 B’的公共传中最长的一个。
Python实现:
def lcs3(seqx,seqy):
table = []
for i in range(len(seqx)+1):
table.append([0]*(len(seqy)+1))
for xline in xrange(1,len(seqx)+1):
for yline in xrange(1,len(seqy)+1):
if seqx[xline-1] == seqy[yline-1] :
table[xline][yline]=table[xline-1][yline-1]+1
else:
table[xline][yline] = table[xline][yline-1] if table[xline][yline-1]>table[xline-1][yline] else table[xline-1][yline]
时间复杂度就是m*n,即n^2
此时,得到的table表,每一个表格的行号和列号对应了序列中的字符位置,(x,y)中的值就是长度为x和y的两个串的最长公共子串的长度。
通过最开始的文件差异对比,到后来的LCS算法,可以发现,通过进行抽象,问题得到了更好的理解和建模。抽象之美:)
分享到:
相关推荐
最长公共子序列(Longest Common Subsequence,简称LCS)是计算机科学中一种经典的问题,主要涉及字符串或序列的比较和分析。这个问题在文本编辑器的差异计算、生物信息学的DNA序列比对以及程序代码的相似性检测等多...
### 最长公共子序列(Longest Common Subsequence, LCS) #### 定义与概念 最长公共子序列问题(LCS)是计算机科学中的一个经典问题,它涉及到在两个或多个序列中寻找最长的相同子序列。这里所说的“子序列”并不...
LCS(longest common substring)算法,即最大公共子串,它是求两个字符串最长公共子串的问题。大体解法是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0。然后求出对角线最长...
Given two sequences X, Y and a constrained sequence P, a sequence Z is a constrained longest common subsequence for X and Y with respect to P if Z is the longest subsequence of X and Y such that P is...
C#,最大公共子序列(LCS,Longest Common Subsequences)的算法与源代码 最长的常见子序列问题是寻找两个给定字符串中存在的最长序列。 最大公共子序列算法,常用于犯罪鉴定、亲子鉴定等等的 DNA 比对。 1.1 子...
GitDiffLCS 通常,git diff ...安装$ gem install git_diff-lcs如何使用$ git_diff_lcs shortstat [GIT_REPOSITORY or WORKING_DIRECTORY] [SRC(branch or commit)] [DEST(branch or commit)]$ git_diff_lcs shortstat ...
今天,我们将讨论动态规划在解决LCS(Longest Common Subsequence)和LIS(Longest Increasing Subsequence)问题中的应用。 首先,让我们来看LCS问题。给定两个序列P1和P2,我们想找到它们的最长公共子序列。为了...
最长公共子序列(Longest Common Subsequence, LCS)问题是计算机科学领域中一个重要的经典问题,它不仅被广泛应用于文本比较、生物信息学中的基因序列比对等领域,而且也是动态规划算法的一个典型应用场景。...
【标题】:“lcsk-master小例子” 涉及的知识点主要集中在“Longest Common Subsequence (LCS) 序列和 SignalR 技术上。Longest Common Subsequence 是一个计算机科学中的经典问题,而 SignalR 则是.NET框架下的实时...
标题 "LCS.zip_LCS 复杂度分析" 指的是一个压缩包文件,其中包含有关Longest Common Subsequence(最长公共子序列)问题的复杂度分析。在这个问题中,给定两个字符串,目标是找到这两个字符串的最长子序列,这个子...
最长公共子序列(Longest Common Subsequence,LCS)是计算机科学中一种经典的问题,主要应用于文本比较、生物信息学等领域。在这个问题中,我们不考虑子序列的顺序,只关心两个序列中是否存在相同的字符,而这些...
这里提到的问题是求解两个字符串的最长公共子序列(Longest Common Subsequence, LCS)。这是一个经典的动态规划问题,对于理解和优化代码能力的考察非常有帮助。 最长公共子序列是指两个字符串中的一个子序列,它...
在IT领域,"LCS"通常指的是“最长公共子序列”(Longest Common Subsequence)算法,这是一种在计算机科学中广泛使用的序列比对方法。在这个名为"LCS.rar"的压缩包文件中,我们找到了一个与LCS算法相关的工程,其目的...
它与最长公共子序列(Longest Common Subsequence,LCS)不同,后者不要求子序列在原字符串中是连续的。 要解决这个问题,我们可以使用动态规划的方法。动态规划是一种通过将大问题分解为更小的子问题来求解的方法...
首先,diff算法的基本思想是通过查找最长公共子序列(Longest Common Subsequence,LCS)来确定两个文本文件的差异。LCS是指在不改变序列原有顺序的情况下,两个序列中最大长度的相同子序列。通过找到LCS,diff可以...
lcs-image-diff使用LCS算法的图像差异库和工具。 murooka的rust端口/ go-diff-image要求最新的Rust(推荐rustup)库lcs-image-diff使用LCS算法的图像diff库和工具。 murooka的rust端口/ go-diff-image要求最新的Rust...
标题中的“lcs.rar_LCS”表明这是一个关于“最长公共子序列(Longest Common Subsequence, LCS)”问题的压缩文件。LCS是计算机科学中一个经典的算法问题,主要涉及字符串处理、序列比较和动态规划等领域。在这个...
最长公共子序列(Longest Common Subsequence,LCS)是计算机科学中一种经典的字符串问题,主要涉及算法设计和分析,特别是在序列比对、文本编辑距离等领域有着广泛应用。在这个问题中,我们有两个给定的字符串,...
本项目“C# LCS 文本比较器”基于最长公共子序列(Longest Common Subsequence,简称LCS)算法,提供了一种高效且灵活的文本比较解决方案。本文将详细介绍LCS算法及其在C#中的实现,以及如何通过该项目进行文本比较...
**最长公共子序列(Longest Common Subsequence, LCS)** 最长公共子序列问题是一个经典的计算机科学问题,主要涉及字符串或序列的比较。在两个给定的字符串中,LCS是指无需考虑字符顺序的最长子串,它同时存在于这...