我正在尝试使用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;
}
}
答案 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;
}