Levenshtein在PHP中使用反向跟踪的距离

时间:2012-10-18 17:14:20

标签: php levenshtein-distance

我正在尝试使用Levenshtein距离算法在PHP中对齐字符串。问题是我的后台跟踪代码不适用于所有情况。例如,当第二个数组在开头插入行时。然后回溯只会到i = 0时。

如何正确实施Levenshtein距离的回溯?

Levenshtein距离,$ s和$ t是字符串数组(行)

function match_rows($s, $t)
{
$m = count($s);
$n = count($t);

for($i = 0; $i <= $m; $i++) $d[$i][0] = $i;
for($j = 0; $j <= $n; $j++) $d[0][$j] = $j;

for($i = 1; $i <= $m; $i++)
{
    for($j = 1; $j <= $n; $j++)
    {
        if($s[$i-1] == $t[$j-1])
        {
            $d[$i][$j] = $d[$i-1][$j-1];
        }
        else
        {
            $d[$i][$j] = min($d[$i-1][$j], $d[$i][$j-1], $d[$i-1][$j-1]) + 1;
        }
    }
}

// backtrace
$i = $m;
$j = $n;
while($i > 0 && $j > 0)
{
    $min = min($d[$i-1][$j], $d[$i][$j-1], $d[$i-1][$j-1]);

    switch($min)
    {
        // equal or substitution
        case($d[$i-1][$j-1]):
            if($d[$i][$j] != $d[$i-1][$j-1])
            {
                // substitution
                $sub['i'][] = $i;
                $sub['j'][] = $j;
            }
            $i = $i - 1;
            $j = $j - 1;
            break;

        // insertion
        case($d[$i][$j-1]):
            $ins[] = $j;
            $j = $j - 1;
            break;

        // deletion
        case($d[$i-1][$j]):
            $del[] = $i;
            $i = $i - 1;
            break;
    }
}

2 个答案:

答案 0 :(得分:3)

这不是挑剔,而是帮助您找到所需的答案(并改进您的实施)。

您使用的算法是Wagner-Fischer算法,而不是Levenshtein算法。此外,Levenshtein距离不用于对齐弦。它严格来说是距离测量。

有两种类型的对齐:全局和本地。全局对齐用于最小化两个整个字符串之间的距离。示例:“REACH”上的全局对齐“RACE”,您将获得“RxACx”。 x是差距。

一般来说,这是Needleman-Wunsch算法,它与Wagner-Fischer算法非常相似。局部对齐在长字符串中查找子字符串,并最小化短字符串与长字符串的子字符串之间的差异。

示例:在“UMBRELLA”上本地对齐“BELL”,并在“BRELL”上对齐“BxELL”。这是Smith-Waterman算法,它再次与Wagner-Fischer算法非常相似。

我希望这有助于您更好地定义所需的确切对齐类型。

答案 1 :(得分:0)

我认为您的错误正是您在问题中所说的:您只要i==0停止,而不是一直到i==0 && j==0。只需替换这个条件:

while($i > 0 && $j > 0)

while ($i > 0 || $j > 0)

你的解决方案已经过了一半。棘手的一点是,如果$i==0,那么在循环体中使用数组索引$i-1是不正确的。因此,您还必须将循环体更改为更像

的内容
while ($i || $j) {
    $min = $d[$i][$j];  // or INT_MAX or something
    if ($i && $j && $min > $d[$i-1][$j-1]) {
        $newi = $i-1;
        $newj = $j-1;
        $min = $d[$newi][$newj];
    }
    if ($i && $min > $d[$i-1][$j]) {
        $newi = $i-1;
        $newj = $j;
        $min = $d[$newi][$newj];
    }
    if ($j && $min > $d[$i][$j-1]) {
        $newi = $i;
        $newj = $j-1;
        $min = $d[$newi][$newj];
    }

    // What sort of transformation is this?
    if ($newi == $i && $newj == $j) {
        assert(false);  // should never happen
    } else if ($newi == $i) {
        // insertion
        $ins[] = $j;
    } else if ($newj == $j) {
        // deletion
        $del[] = $i;
    } else if ($d[$i][$j] != $d[$newi][$newj]) {
        // substitution
        $sub['i'][] = $i;
        $sub['j'][] = $j;
    } else {
        // identity
    }

    assert($newi >= 0); assert($newj >= 0);
    $i = $newi;
    $j = $newj;
}