Diff算法研究

2 篇文章 0 订阅
  1. http://en.wikipedia.org/wiki/Diff
  2. http://en.wikipedia.org/wiki/Longest_common_subsequence_problem
  3. http://en.wikipedia.org/wiki/Dynamic_programming
  4. http://xw2k.nist.gov/dads/

在Unix/Linux的世界里面,如果我们需要比较两个文件,就会用一个比较的命令——diff。而这个diff的原理是什么呢?

 

在diff里面,我们比较的两个文件叫做old和new,而一般是按行来比较。这里我们可以抽象成一个字符串的比较,比如:

old: abcdefger

new: abdefereger

那么其中的每一个字符都可以表示文件里面的一行。那么diff里面用到的比较思想是从old和new里面找出最长的subsequence。

subsequence的定义是:如果原串是(a1, a2, ..., an)的话,其中ai表示串中的第i个字符,那么(a[m1], a[m2], ..., a[mi])称为一个subsequence,如果m1, m2, ..., mi是[1, n]的元素,m1 < m2 < ...< mi且a[mi] 属于ai里面的字符。

那么diff的任务就是找出old和new里面的最长公共subsequence(Longest common subsequence/LCS)。

For example:

old: fabc

new: ebca

那么LCS就是bc。

 

那么就可以设计一个函数lsp,他接受两个字符串作为参数,返回他们的LSP。

string lsp(const string &s1, const string &s2);

 

同时,在我们对比字符串的时候,我们都是从前往后的对比的,那么我们可以得到下面的性质。

lsp(s1, s2) = 1). s1[0] + lsp(tail(s1), tail(s2))   if s1[0] == s2[0]

                   2). max{lsp(s1, tail(s2)), lsp(tail(s1), s2)} if s1[0] != s2[0]

其中tail(str)表示str的除了第一个字符之外的剩下的子串。

可以看出来上面的公式是一个递归的suboptimal公式,这正是Dynamic Programming(DP)里面的一个思想。所以我们就可以用DP来解决上面的问题。

在这里我用的是Bottom-up[3]的DP思路,也就是从最基本的case开始构造元素,使得在后面的计算里面可以重复的利用之前的计算。

假设我们开辟一个2维数据,他的列标志(Column index)表示old里面的字符,比如说old是fabc,0是空字符'/0',1是'c',2是'b',3是'a',4是'f'。字母顺序是反过来的。

而行标志(Row Index)表示new里面的字符,比如说new是ebca,0是空字符'/0',1是'a',2是'c',3是'b',4是'e'。

我们定义oldChars[old.size() + 1]为old里面的字符, newChars[new.size() + 1]为new里面的字符,里面的内容就和上面说的一样。然后定义table[old.size() + 1][new.size() + 1]是存放LCS的一个2维数组。那么通过上面的lsp公式,我们可以知道两个字串的LSP是: 1) 如果首个字母是相同的话,也就是oldChars[i] == newChars[j]的时候,那么LSP就是oldChars[i] + table[i - 1][j - 1]; 2) 如果两个首字母是不一样的话,也就是newChars[i] != oldChars[j]的话,那么LSP就是max(table[i - 1][j - 1], table[i][j - 1])。其中比较的标准是字串较长的为较大的字串,如果长度相等,那么就是按字典序排序的较大者为较大者。

从而我们可以这样计算:

 


Function lsp(old, new)

oldSize = old.size()

newSize = new.size()


for i -> 0 to oldSize

   for j -> 0 to newSize

      if (oldChars[i] == newChars[j])

          then table[i][j] := Append(oldChars[i], table[i - 1][j - 1])

      else

          table[i][j] = Max(table[i - 1][j], table[i][j - 1])

return table[old.size()][new.size()]

 


注意,table[i][0]和table[0][j]都被预先赋为空串""了。

通过这样的计算,最后的table[old.size()][new.size()]就是所求的LCS。

在得到了LSP之后,只要比较old和LSP,不同的就是被删除了的;比较new和LSP,不同的就是新增加的。

这个DP算法的时间复杂度是O(nm),空间复杂度是O(nm),不过空间复杂度可以简化到O(n)。

下面是完整的一段代码:

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值