使用PHP levenshtein 功能比较字符串我已经取得了一些成功。

但是,对于包含已交换位置的子串的两个字符串,算法会将这些字符串计为全新的子字符串。

例如:

levenshtein("The quick brown fox", "brown quick The fox"); // 10 differences

被视为不太常见而不是:

levenshtein("The quick brown fox", "The quiet swine flu"); // 9 differences

我更喜欢一种算法,它看到前两个更相似。

我怎样才能提出一个比较函数,它可以识别将位置切换为与编辑不同的子串?

我想到的一种可能的方法是在比较之前将字符串中的所有单词按字母顺序排列。这使得单词的原始顺序完全脱离了比较。然而,这样做的一个缺点是,只更改一个单词的第一个字母可能会造成比单个字母更改所造成的更大的中断。

我想要实现的是比较两个关于自由文本字符串的人的事实,并确定这些事实表明相同事实的可能性。事实可能是有人上学的学校,例如雇主或出版商的名字。两个记录可能有相同的学校拼写不同,单词顺序不同,额外的单词等,所以如果我们要好好猜测他们指的是同一所学校,匹配必须有些模糊。到目前为止,它在拼写错误方面表现得非常好(我使用的是一种类似于metaphone的phoenetic算法),但是如果你改变学校中常见的单词顺序则非常糟糕:“xxx college” vs“xxx大学”。

有帮助吗?

解决方案

的n-gram

使用支持倍数的 N-gram 整个文本中的字符转置

一般的想法是你将所讨论的两个字符串分成所有可能的2-3个字符子串(n-gram),并将两个字符串之间的共享n-gram数量作为它们的相似性度量。然后可以通过将共享数除以较长字符串中的n-gram总数来对其进行归一化。这很难计算,但相当强大。

对于例句:

A. The quick brown fox
B. brown quick The fox
C. The quiet swine flu

A和B分享 18 2克

A和C只分享 8 2克

20 总数。

这已在 Gravano等人更详细地讨论过。纸

tf-idf和余弦相似度

一个不那么琐碎的替代方案,但以信息理论为基础将使用术语术语频率–逆文档频率(tf-idf)权衡令牌,构造句子向量然后使用余弦相似度作为相似度量。

算法是:

  1. 计算每个句子的2个字符的令牌频率(tf)。
  2. 计算逆句频率(idf),它是语料库中所有句子数(在这种情况下为3)的商的对数除以特定标记在所有句子中出现的次数。在这种情况下, th 在所有句子中,因此它具有零信息内容(log(3/3)= 0)。
  3. 通过乘以tf和idf表中的相应单元格来生成tf-idf矩阵。
  4. 最后,计算所有句子对的余弦相似度矩阵,其中A和B是来自tf-idf表的权重,用于相应的标记。范围从0(不相似)到1(相等)。

  5. Levenshtein修改和Metaphone

    关于其他答案。 Damerau– Levenshtein 修改仅支持两个的转换相邻的字符。 Metaphone 旨在匹配听起来相同而不是 用于相似性匹配。

其他提示

很容易。只需使用 Damerau-Levenshtein 距离而不是字母。

爆炸空间,对数组进行排序,内爆,然后进行Levenshtein。

您也可以试试这个。 (只是一个额外的建议)

$one = metaphone("The quick brown fox"); // 0KKBRNFKS
$two = metaphone("brown quick The fox"); // BRNKK0FKS
$three = metaphone("The quiet swine flu"); // 0KTSWNFL

similar_text($one, $two, $percent1); // 66.666666666667
similar_text($one, $three, $percent2); // 47.058823529412
similar_text($two, $three, $percent3); // 23.529411764706

这将显示第1和第2个比第1和第3,第2和第3更相似。

我一直在拼写检查器中实施levenshtein。

你所要求的是将换位计为1次编辑。

如果您只想计算一个单词的换位,这很容易。然而,对于2个或更多单词的转换,算法的添加是最坏情况!(max(wordorder1.length(),wordorder2.length()))。将非线性子算法添加到已经二次的算法中并不是一个好主意。

这就是它的工作原理。

if (wordorder1[n] == wordorder2[n-1])
{
  min(workarray[x-1, y] + 1, workarray[x, y-1] + 1, workarray[x-2, y-2]);
}
  else
{
  min(workarray[x-1, y] + 1, workarray[x, y-1] + 1);
}

只是为了触摸换位。如果你想要所有的换位,你必须从那个位置开始向后工作,比较

1[n] == 2[n-2].... 1[n] == 2[0]....

所以你明白他们为什么不在标准方法中包含它。

这个答案并做出以下改变:

void match(trie t, char* w, string s, int budget){
  if (budget < 0) return;
  if (*w=='\0') print s;
  foreach (char c, subtrie t1 in t){
    /* try matching or replacing c */
    match(t1, w+1, s+c, (*w==c ? budget : budget-1));
    /* try deleting c */
    match(t1, w, s, budget-1);
  }
  /* try inserting *w */
  match(t, w+1, s + *w, budget-1);
  /* TRY SWAPPING FIRST TWO CHARACTERS */
  if (w[1]){
    swap(w[0], w[1]);
    match(t, w, s, budget-1);
    swap(w[0], w[1]);
  }
}

这是用于trie中的字典搜索,但是为了匹配单个单词,它是相同的想法。你正在进行分支,并且在任何时候,你可以做任何你喜欢的改变,只要你付出代价。

消除两个字符串之间的重复单词,然后使用Levenshtein。

我相信这是使用向量空间搜索引擎的一个主要示例。

在这种技术中,每个文档基本上都成为一个具有与整个语料库中不同单词一样多的维度的向量;然后类似的文档占据该向量空间中的相邻区域。这个模型的一个不错的属性是查询也只是文档:要回答查询,您只需计算它们在向量空间中的位置,结果就是您可以找到的最接近的文档。我确信那里有PHP的“即将推出”的解决方案。

从矢量空间模糊化结果,您可以考虑使用词干/类似的自然语言处理技术,并使用levenshtein为您的整体词汇中出现的类似单词构建辅助查询。

如果第一个字符串是A而第二个字符串是B:

  1. 将A和B分成单词
  2. 对于A中的每个单词,找到B中最匹配的单词(使用levenshtein)
  3. 从B中删除该单词并将其放在B *中与A中匹配单词的索引相同。
  4. 现在比较A和B *
  5. 示例:

    A: The quick brown fox
    B: Quick blue fox the
    B*: the Quick blue fox
    

    您可以通过多次传递来改进第2步,首先只找到完全匹配,然后找到A中没有B *中的伴侣的单词的紧密匹配,然后是较少的匹配,等等。 / p>

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top