PHP中的字符串相似性:长字符串的levenshteinlike函数
Posted
技术标签:
【中文标题】PHP中的字符串相似性:长字符串的levenshteinlike函数【英文标题】:String similarity in PHP: levenshtein like function for long strings 【发布时间】:2011-07-02 20:06:59 【问题描述】:php 中的函数levenshtein
适用于最大长度为 255 的字符串。在 PHP 中计算句子相似度得分的好方法是什么。
基本上我有一个句子数据库,我想找到近似的重复项。
similar_text
函数没有给我预期的结果。我检测如下类似句子的最简单方法是什么:
$ss="Jack is a very nice boy, isn't he?";
$pp="jack is a very nice boy is he";
$ss=strtolower($ss); // convert to lower case as we dont care about case
$pp=strtolower($pp);
$score=similar_text($ss, $pp);
echo "$score %\n"; // Outputs just 29 %
$score=levenshtein ( $ss, $pp );
echo "$score\n"; // Outputs '5', which indicates they are very similar. But, it does not work for more than 255 chars :(
【问题讨论】:
注意:我不关心语义。 【参考方案1】:我发现Smith Waterman Gotoh 是比较句子的最佳算法。更多信息in this answer。下面是 PHP 代码示例:
class SmithWatermanGotoh
private $gapValue;
private $substitution;
/**
* Constructs a new Smith Waterman metric.
*
* @param gapValue
* a non-positive gap penalty
* @param substitution
* a substitution function
*/
public function __construct($gapValue=-0.5,
$substitution=null)
if($gapValue > 0.0) throw new Exception("gapValue must be <= 0");
//if(empty($substitution)) throw new Exception("substitution is required");
if (empty($substitution)) $this->substitution = new SmithWatermanMatchMismatch(1.0, -2.0);
else $this->substitution = $substitution;
$this->gapValue = $gapValue;
public function compare($a, $b)
if (empty($a) && empty($b))
return 1.0;
if (empty($a) || empty($b))
return 0.0;
$maxDistance = min(mb_strlen($a), mb_strlen($b))
* max($this->substitution->max(), $this->gapValue);
return $this->smithWatermanGotoh($a, $b) / $maxDistance;
private function smithWatermanGotoh($s, $t)
$v0 = [];
$v1 = [];
$t_len = mb_strlen($t);
$max = $v0[0] = max(0, $this->gapValue, $this->substitution->compare($s, 0, $t, 0));
for ($j = 1; $j < $t_len; $j++)
$v0[$j] = max(0, $v0[$j - 1] + $this->gapValue,
$this->substitution->compare($s, 0, $t, $j));
$max = max($max, $v0[$j]);
// Find max
for ($i = 1; $i < mb_strlen($s); $i++)
$v1[0] = max(0, $v0[0] + $this->gapValue, $this->substitution->compare($s, $i, $t, 0));
$max = max($max, $v1[0]);
for ($j = 1; $j < $t_len; $j++)
$v1[$j] = max(0, $v0[$j] + $this->gapValue, $v1[$j - 1] + $this->gapValue,
$v0[$j - 1] + $this->substitution->compare($s, $i, $t, $j));
$max = max($max, $v1[$j]);
for ($j = 0; $j < $t_len; $j++)
$v0[$j] = $v1[$j];
return $max;
class SmithWatermanMatchMismatch
private $matchValue;
private $mismatchValue;
/**
* Constructs a new match-mismatch substitution function. When two
* characters are equal a score of <code>matchValue</code> is assigned. In
* case of a mismatch a score of <code>mismatchValue</code>. The
* <code>matchValue</code> must be strictly greater then
* <code>mismatchValue</code>
*
* @param matchValue
* value when characters are equal
* @param mismatchValue
* value when characters are not equal
*/
public function __construct($matchValue, $mismatchValue)
if($matchValue <= $mismatchValue) throw new Exception("matchValue must be > matchValue");
$this->matchValue = $matchValue;
$this->mismatchValue = $mismatchValue;
public function compare($a, $aIndex, $b, $bIndex)
return ($a[$aIndex] === $b[$bIndex] ? $this->matchValue
: $this->mismatchValue);
public function max()
return $this->matchValue;
public function min()
return $this->mismatchValue;
$str1 = "Jack is a very nice boy, isn't he?";
$str2 = "jack is a very nice boy is he";
$o = new SmithWatermanGotoh();
echo $o->compare($str1, $str2);
【讨论】:
【参考方案2】:levenshtein
算法的时间复杂度为O(n*m)
,其中n
和m
是两个输入字符串的长度。这是相当昂贵的,对于长字符串计算这样的距离需要很长时间。
对于整个句子,您可能希望使用diff
算法,例如:Highlight the difference between two strings in PHP
话虽如此,PHP 还提供了 similar_text
函数,它的复杂性更差 (O(max(n,m)**3)
),但似乎可以处理更长的字符串。
【讨论】:
谢谢。你能告诉我我是否错误地解释了similar_text的结果吗?我希望能够检测到示例中类似的句子。similar_text
返回匹配字符的数量,而不是百分比。还有第三个可选参数可用于查找百分比。
哦好吧..我认为第三个参数只是如果你想通过引用传递结果变量:)。【参考方案3】:
您可以尝试使用similar_text。 使用 20,000 多个字符(3-5 秒)可能会变得非常慢,但是您提到的示例仅使用句子,这对于这种用法会很好。
需要注意的一点是,在比较不同大小的字符串时,您不会得到 100%。例如,如果您将“he”与“head”进行比较,您只会得到 50% 的匹配。
【讨论】:
以上是关于PHP中的字符串相似性:长字符串的levenshteinlike函数的主要内容,如果未能解决你的问题,请参考以下文章