我正在使用Levenshtein算法来找到两个字符串之间的相似性。这是我正在制作的计划中非常重要的一部分,因此它需要有效。 问题是该算法没有找到类似的以下示例:
CONAIR
AIRCON
该算法将给出6的距离。因此,对于6个字母的单词(您查看具有最高字母数量的单词),差异为100%=>相似度为0%。
我需要找到一种方法来找到两个字符串之间的相似之处,同时也要考虑像我之前提到的那样的情况。
我可以使用更好的算法吗?或者你们有什么推荐我的?
编辑:我还研究了“Damerau-Levenshtein”算法,该算法增加了换位。问题是这个换位仅适用于相邻的字符(而不适用于多个字符)。答案 0 :(得分:10)
我会将这个词分为unigrams,bigrams和trigrams,然后计算余弦相似度。
答案 1 :(得分:5)
我认为这可以通过在其中一个字符串上使用最长公共子字符串/子序列算法(例如“conair”)和另一个附加到其自身的字符串(例如“aircon”)来轻松解决 - >“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%
答案 2 :(得分:2)
听起来你可能想尝试使用音节或音素代替字母来做Levenshtein距离。
答案 3 :(得分:2)
理论上,您使用的方法对于您尝试解决的问题是正确的。但是Levenstein只会考虑两组中的个性。
也可以使用Longest Common Subsequence方法找到字符串的相似性,然后你可以看到Levenstein在其余的不匹配上。
如果您想要采用聚类方法,the following answer似乎有一些细节,但显然实施起来更困难。
答案 4 :(得分:2)
对单词进行排序并找到Levenshtein将为您的示例提供100%的匹配,但它也会给出100%的匹配,例如
CONAIR
RCIAON
可能不是你想要的。
定义相似性的另一种方法是找出2个字符串的公共子串。您可以创建Suffix Tree并查找所有常见的子字符串,并尝试确定它们的相似程度。所以对你的后缀树会给出常见的子串,如CON&amp; AIR覆盖整个单词(对于你的2个字符串),从而将它们视为相似的。
答案 5 :(得分:2)
尝试使用其他相似性测量,如sorenson,jaccard和jaro_winkler
我个人非常喜欢jaro winkler,因为它多次服务于我的目的。
from Levenshtein import jaro_winkler
In [2]: jaro_winkler("conair","aircon")
Out[2]: 0.8333333333333334
答案 6 :(得分:1)
看看Needleman-Wunsch或Smith-Waterman算法。它们用于通过适应DNA序列的编辑距离来处理字符串匹配,其中任何类型的插入,反转,转座子可以发生在任何地方的任何长度。说这个,我需要添加一个足够长的字符串,没有最佳解决方案。并且不要忘记编辑成本取决于算法的使用上下文(语义问题),而任何算法总是语法机器。