串相似性 - > Levenshtein距离(String similarity -> Lev

2019-06-26 13:23发布

我使用Levenshtein算法来找到两个字符串之间的相似性。 这是我在做节目的一个非常重要的一部分,所以它需要是有效的。 问题是,该算法没有找到下面的例子类似:

CONAIR
空调

该算法将给出6.距离所以对于6个字母(你看与字母最高量的字),所不同的是100%=>的相似性为0%这个词。

我需要找到一种方法来找到两个字符串之间的相似性,同时也考虑到案件像我之前提出。

有没有更好的算法,我可以使用吗? 或者,你们怎么推荐我吗?

编辑:我也进去看了“Damerau - 莱文斯坦”算法,它增加了换位。 的问题是,这种换位仅用于相邻字符(而不是一个数量的字符)。

Answer 1:

我会分裂术语为对unigram,二元语法和卦,然后计算余弦相似度。



Answer 2:

我认为这可以通过采用在弦上的一个最长公共子串/后算法(例如“Conair公司”),并追加到自身,一旦其他字符串可以轻松解决(如“空调” - >“airconaircon”)。

在C样品代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// Returns the length of the longest common substring (LCS)
// between two given strings.
//
// This recursive implementation can be replaced by a
// more performant dynamic programming implementation.
size_t llcs(const char* s1, const char* s2)
{
  size_t len[3];

  if (*s1 == '\0' || *s2 == '\0') return 0;

  len[0] = (*s1 == *s2) + llcs(s1 + 1, s2 + 1);
  len[1] = llcs(s1 + 1, s2);
  len[2] = llcs(s1, s2 + 1);

  if (len[0] < len[1]) len[0] = len[1];
  if (len[0] < len[2]) len[0] = len[2];

  return len[0];
}

// Returns similarity of two given strings in the range
// from 0.0 to 1.0 (1.0 for equal strings).
double similarity(const char* s1, const char* s2)
{
  size_t s1len = strlen(s1);
  size_t s2len = strlen(s2);
  double sim;

  if (s1len == 0 && s2len == 0)
  {
    // Two empty strings are equal
    sim = 1;
  }
  else
  {
    size_t len;
    // Append s1 to itself in s1s1 (e.g. "aircon" -> "airconaircon")
    char* s1s1 = malloc(s1len * 2 + 1);
    strcpy(s1s1, s1);
    strcpy(s1s1 + s1len, s1);

    // Find the length of the LCS between s1s1 and s2
    // (e.g. between "airconaircon" and "conair")
    len = llcs(s1s1, s2);
    // We need it not longer than s1 (e.g. "aircon")
    // since we're actually comparing s1 and s2
    if (len > s1len) len = s1len;

    len *= 2;

    // Prevent 100% similarity between a string and its
    // cyclically shifted version (e.g. "aircon" and "conair")
    if (len == s1len + s2len && strcmp(s1, s2) != 0) len--;

    // Get the final measure of the similarity
    sim = (double)len / (s1len + s2len);

    free(s1s1);
  }

  return sim;
}

int main(int argc, char** argv)
{
  if (argc == 3)
    printf("Similarity of \"%s\" and \"%s\" is %.2f%%\n",
           argv[1], argv[2], 100 * similarity(argv[1], argv[2]));
  else
    printf("Usage:\n  %s string1 string2\n",
           argv[0]);
  return 0;
}

输出示例:

Similarity of "123" and "123" is 100.00%
Similarity of "123" and "1234" is 85.71%
Similarity of "0123" and "123" is 85.71%
Similarity of "a" and "aa" is 66.67%
Similarity of "aa" and "a" is 66.67%
Similarity of "aaaaaaa" and "aaaaaa" is 92.31%
Similarity of "aaaaaa" and "aaaaaaa" is 92.31%
Similarity of "aircon" and "conair" is 91.67%
Similarity of "spit" and "pits" is 87.50%
Similarity of "pits" and "spit" is 87.50%
Similarity of "spits" and "pits" is 88.89%
Similarity of "pits" and "spits" is 88.89%


Answer 3:

这听起来像你可能想尝试使用音节或音素的不是字母做Levenshtein距离。



Answer 4:

从理论上讲,你正在使用的方法是,你正在试图解决的问题是正确的。 但是,莱文施泰因将只考虑单个字符的两套。

串相似度也可以使用发现最长公共子序列的方法,然后你可以看到无与伦比的休息莱文施泰因。

如果你想做一个集群的方式, 下面的回答似乎有一些细节,但显然是更难以实现。



Answer 5:

排序的话,寻找莱文斯坦会给您例如100%的比赛,但它也给了100%匹配如

CONAIR
RCIAON

这可能不是你想要的。

另一种方法来定义相似性会找出共同子串2串。 您可以创建一个后缀树 ,找出所有常见字符串,并试图确定他们是多么相似。 因此,对于你如后缀树会给普通子为CON和AIR覆盖整个字(您2串),因此他们的结论相似。



Answer 6:

尝试使用其他类似措施,如索伦森,捷卡和jaro_winkler

我个人是哈罗温克勒的忠实粉丝,因为它曾我的目的了很多次。

from Levenshtein import jaro_winkler
In [2]: jaro_winkler("conair","aircon")
Out[2]: 0.8333333333333334


Answer 7:

看看以尼德曼 - 翁施,或史密斯 - 沃特曼算法。 它们被用来通过适于编辑距离处理字符串匹配用于DNA序列,其中任何形式的插入,倒转,转座子可以发生任何长度,在任何地方。 话说到此,我需要补充的是,一个足够长的字符串没有最优解。 而且不要忘记,编辑成本取决于算法(一种语义问题)的使用情境中,而任何算法始终是一个语法机。



文章来源: String similarity -> Levenshtein distance