从最长公共子序列打印差异

时间:2015-04-09 14:17:52

标签: python perl diff lcs

所以我一直在为Perl和amp;做一些练习问题。 Python(有点在2之间选择)和我有一个问题,我需要像Github一样制作我自己的diff算法。 我已经知道最长公共子序列问题是解决方案的重要部分。我使用wikipedia page for LCS作为参考,但我仍然无法弄清楚差异部分。

我也意识到CPAN上已有模块,比如Algorithm:Diff,但这主要是为了练习而且感觉像是在作弊。

我想出了算法的python / pseudocode版本,但我打算用多维数组来做,Perl似乎没有。

现在我可以在Perl中成功获得最长公共子序列长度。

基本上伪代码(几乎像python一样,但应该用于Perl)我能想到的是这样的:

function lengthOfLCS(string1, string2){
    if length(string1) == 0 or length(string2) == 0:
         return 0
    else if string1[0] eq string2[0]: 
         return 1+ lengthOfLCS(stringA[1:], stringB[1:])
    return max(lengthOfLCS(string1, string2[1:], lengthOfLCS(string1[1:], string2))

我还没有实现它,但我认为基本上我可以如何计算两个字符串的LCS长度?

输出方面,它应该返回4对“人类”& “CHIMPANZEE”(LCS = HMAN)

所以我要问的是,从现在开始如何使用Perl打印Diffs?我知道不是只有LCS的长度,而是应该返回一个List / Array,这可以通过在LCS函数中返回一个多维列表然后在单独的diff函数中处理它来实现。

我是Perl的新手,所以任何指针/提示都会非常感激。 感谢。

1 个答案:

答案 0 :(得分:0)

你可以在Perl中使用我的LCS参考实现,它需要两个数组引用作为输入,并返回一个包含匹配元素索引的两个元素数组的数组。

use LCS;
my $lcs = LCS->LCS( [qw(a b)], [qw(a b b)] );
# $lcs now contains an arrayref of matching positions
# same as
$lcs = [
  [ 0, 0 ],
  [ 1, 2 ]
];

LCS使用传统算法并迭代地读出LCS(参见我的博客文章Loopify Recursions at wollmers-perl.blogspot.de),即不是递归的(大多数示例代码使用递归,这在Perl中不能很好地扩展)。因此,如果您想从代码中学习,请查看子LCS()和_lcs()。

如果你想要diff,即编辑脚本,你可以从LCS数组重构它。

方法lcs2align()几乎就是这样做的。

use Data::Dumper;
use LCS;
print Dumper(
  LCS->lcs2align(
    [qw(a   b)],
    [qw(a b b)],
    LCS->LCS([qw(a b)],[qw(a b b)])
  )
);
# prints
$VAR1 = [
          [
            'a',
            'a'
          ],
          [
            '',
            'b'
          ],
          [
            'b',
            'b'
          ]
];

sdiff()(see Algorithm::Diff)格式的差异现在看起来像:

[
  [ 'u', 'a', 'a'  ],
  [ '+', '',  'b'  ],
  [ 'u', 'b', 'b'  ],
]

如何从对齐中获取编辑脚本应该是微不足道的,并留作练习。

如果你想要更快的实现,你可以使用LCS :: Tiny,或者最快的纯Perl实现LCS :: BV,或者最快的扩展算法算法:: Diff :: XS(参见我的博客文章Tuning Algorithm :: Diff at wollmers-perl.blogspot.de)

请记住,基于LCS的编辑脚本不会自动提供SES(最短的编辑脚本)。 LCS基于编辑操作,仅允许插入和删除(简单编辑距离)。 SES算法通常最小化Levenshtein距离(插入,删除和不匹配)。