在许多字符串列表中查找类似字符串的算法

时间:2015-02-03 17:24:24

标签: algorithm

我知道近似字符串搜索和Levenshtein距离之类的东西,但我想要做的是获取大量字符串并快速挑选出彼此相似的任何匹配对(例如,1 D​​amerau-Levenshtein距离)除)。像这样的东西

l = ["moose", "tiger", "lion", "mouse", "rat", "fish", "cat"]

matching_strings(l)

# Output
# [["moose","mouse"],["rat", "cat"]]

我真的只知道如何使用R和Python,如果你的解决方案可以用其中一种语言轻松实现,那么就可以获得奖励。

更新:

感谢Collapsar的帮助,这是Python的解决方案

import numpy
import functools
alphabet = {'a': 0, 'c': 2, 'b': 1, 'e': 4, 'd': 3, 'g': 6, 'f': 5, 'i': 8, 'h': 7, 'k': 10, 'j': 9, 'm': 12, 'l': 11, 'o': 14, 'n': 13, 'q': 16, 'p': 15, 's': 18, 'r': 17, 'u': 20, 't': 19, 'w': 22, 'v': 21, 'y': 24, 'x': 23, 'z': 25}


l = ["moose", "tiger", "lion", "mouse", "rat", "fish", "cat"]
fvlist=[]

for string in l:
    fv = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
    for letter in string:
        fv[alphabet[letter]]+=1
    fvlist.append(fv)

fvlist.sort (key=functools.cmp_to_key(lambda fv1,fv2: numpy.sign(numpy.sum(numpy.subtract(fv1, fv2)))))

但是,排序的向量按以下顺序返回:

“老鼠”“猫”“狮子”“鱼”“驼鹿”“老虎”“老鼠”

我认为这是次优的,因为我希望驼鹿和鼠标最终彼此相邻。我明白,不过我对这些词进行排序,就无法将所有单词放在所有最近的单词旁边。但是,我仍然愿意接受其他解决方案

3 个答案:

答案 0 :(得分:3)

一种方法(复杂O(n k^2),其中n是字符串数,k是最长的字符串)是将每个字符串转换为一组这样的掩码:

rat => ?at, r?t, ra?, ?rat, r?at, ra?t, rat?

这样一来,如果一个字母中的两个单词不同,比如'rat'和'cat',它们都会有掩码?at等,而如果一个单词是另一个单词的子序列,就像'rat '和'老鼠',他们都会戴面具'老鼠?'。

然后,您只需根据其掩码对字符串进行分组,并打印具有两个以上字符串的组。如果数组有重复数据,您可能希望首先对数组进行重复数据删除。

这是一个示例代码,其中包含额外的cats字符串。

l = ["moose", "tiger", "lion", "mouse", "rat", "fish", "cat", "cats"]
d = {}

def add(mask, s):
    if mask not in d:
        d[mask] = []
    d[mask].append(s)

for s in l:
    for pos in range(len(s)):
        add(s[:pos] + '?' + s[pos + 1:], s)
        add(s[:pos] + '?' + s[pos:], s)
    add(s + '?', s)

for k, v in d.items():
    if len(v) > 1:
        print v

输出

['moose', 'mouse']
['rat', 'cat']
['cat', 'cats']

答案 1 :(得分:1)

天真的实现相当于设置一个由字符串索引的布尔矩阵(即它们在排序列表中的位置)并比较每对字符串,如果字符串为&#,则将相应的矩阵元素设置为true 39;类似'符合你的标准。这将在O(n^2)

中运行

通过将字符串转换为字符频率元组(例如'moose' - > (0,0,0,0,1,0,0,0,0,0,0,0,1,0,2,0,0,0,1,0,0,0,0,0,0,0)i - 向量组件代表i,您可能会感觉更好 - 字母表中的字母)。请注意,频率矢量会因“少数几个”而有所不同。仅限组件(例如,对于最多2个组件中的D-L距离1,各自的差异为+ 1,-1)。

对转换后的数据进行排序。您希望生成的对的候选人将相邻或至少“关闭”。在已排序的转换值列表中彼此相互关联。您可以通过将每个列表条目与列表中最多k个后继项进行比较来检查候选项,k是一个小整数(当然,实际比较相应的字符串)。该算法将在O(n log n)

中运行

您必须在转换/排序的额外开销(使用复杂的比较操作,取决于您为频率向量选择的表示)和减少的比较数之间进行权衡。该方法不考虑字符的字内位置,而只考虑它们的出现。根据实际的字符串集,有许多候选对不会变成实际上类似的字符串。对

由于您的数据似乎由英语词汇组成,潜在的优化是定义字符类(例如元音/辅音,' /其他元音/音节辅音/非音节辅音)而不是个性。

其他优化:

请注意,数据集中彼此排列的字符串对(例如[arttar])将在给定的转换下生成相同的值。因此,如果您将自己限制为1 的D-L距离,如果不考虑将相邻字符转置为单个编辑步骤,则永远不要选择具有与候选相同的转换值的列表项。

答案 2 :(得分:1)

第一步,您必须使用任何模糊搜索索引索引列表。

第二,您需要迭代列表并通过在预索引列表中快速查找来搜索邻居。

关于模糊索引:

大约15年前我写过模糊搜索,可以找到N关闭邻居。这是我对Wilbur的三元组算法的修改,这个修改被命名为" Wilbur-Khovayko算法"。

基本思路:通过三元组分割字符串,并搜索最大交集分数。

例如,让我们有字符串" hello world"。这个字符串生成三元组:helll llo" lo"," o_w"等等;此外,为每个单词生成特殊的前缀/后缀三元组,例如$ he $ wo lo $ ld $。

此后,对于每个三元组构建的索引,它存在于哪个术语中。

因此,这是每个trigram的term_ID列表。

当用户调用某个字符串时 - 它也会分割为三元组,并编程搜索最大交集分数,并生成N个大小的列表。

它工作得很快:我记得,在旧的Sun / Solaris上,256MB RAM,200MHZ CPU,它在字典5,000,000个术语中搜索100个最近的术语,0.25s

您可以从http://olegh.ftp.sh/wilbur-khovayko.tgz

获取旧源代码