寻找类似字符串的优化方式

时间:2013-11-17 18:17:14

标签: algorithm search optimization

假设我有一大堆单词。 (大约4-5千和增加)。有人搜索了一个字符串。不幸的是,在wordlist上找不到字符串。现在找到与输入字符串类似的单词的最佳和优化方法是什么?我想到的第一件事是计算词汇表的每个条目和输入字符串之间的Levenshtein距离。但这是优化的方式吗?

(请注意,这不是语言特定的问题)

3 个答案:

答案 0 :(得分:1)

我认为存在比这更好的东西,但BK树至少是蛮力的优化。

它使用Levenshtein距离作为度量空间的属性,因此如果你的d和任意字符串query之间的Levenshtein距离为s,那么您的所有结果必须与(d+n) to (d-n)s保持一定距离。这里n是要输出的查询的最大Levenshtein距离。

这里详细解释:http://blog.notdot.net/2007/4/Damn-Cool-Algorithms-Part-1-BK-Trees

答案 1 :(得分:1)

如果您对代码本身感兴趣,我实现了一种算法,用于查找两个字符串之间的最佳对齐方式。它基本上展示了如何使用k操作将一个字符串转换为另一个字符串(其中k是字符串的Levenstein/Edit Distance)。它可以根据您的需求进行简化(因为您只需要距离本身)。顺便说一句,它适用于O(mn),其中mn是字符串的长度。我的实施基于:thisthis

#optimalization: using int instead of strings:
#1 ~ "left", "insertion"
#7 ~ "up", "deletion"
#17 ~ "up-left", "match/mismatch"
def GetLevenshteinDistanceWithBacktracking(sequence1,sequence2):

distances = [[0 for y in range(len(sequence2)+1)] for x in range(len(sequence1)+1)]
backtracking = [[1 for y in range(len(sequence2)+1)] for x in range(len(sequence1)+1)]

for i in range(1, len(sequence1)+1):
    distances[i][0]=i
for i in range(1, len(sequence2)+1):
    distances[0][i]=i

for j in range(1, len(sequence2)+1):
    for i in range(1, len(sequence1)+1):
        if sequence1[i-1] == sequence2[j-1]:
            distances[i][j]=distances[i-1][j-1]
            backtracking[i][j] = 17
        else:
            deletion = distances[i-1][j]+1
            substitution = distances[i-1][j-1]+1
            insertion = distances[i][j-1] + 1
            distances[i][j]=min(  deletion, substitution, insertion)

            if distances[i][j] == deletion:
                backtracking[i][j] = 7
            elif distances[i][j] == insertion:
                backtracking[i][j] = 1
            else:
                backtracking[i][j] = 17

return (distances[len(sequence1)][len(sequence2)], backtracking)

def Alignment(sequence1, sequence2):

cost, backtracking = GetLevenshteinDistanceWithBacktracking(sequence1, sequence2)

alignment1 = alignment2 = ""

i = len(sequence1)
j = len(sequence2)

#from backtracking-matrix we get optimal-alignment
while(i > 0 or j > 0):
    if i > 0 and j > 0 and backtracking[i][j] == 17: 
        alignment1 = sequence1[i-1] + alignment1
        alignment2 = sequence2[j-1] + alignment2
        i -= 1
        j -= 1
    elif i > 0 and backtracking[i][j] == 7:
        alignment1 = sequence1[i-1] + alignment1
        alignment2 = "-" + alignment2
        i -= 1
    elif j > 0 and backtracking[i][j]==1:
        alignment2 = sequence2[j-1] + alignment2
        alignment1 = "-" + alignment1
        j -= 1
    elif i > 0:
        alignment1 = sequence1[i-1] + alignment1
        alignment2 = "-" + alignment2
        i -= 1

return (cost, (alignment1, alignment2))

这取决于更广泛的背景以及您希望的准确程度。但我会(可能)开始:

  1. 仅考虑以与查询字相同的字符开头的子集。对于单个查询,它会减少大约20个工作量。
  2. 我会根据长度对单词进行分类,每个类别都会允许最大距离为不同的数字。如果是4类,例如: 0 - 如果长度在0和2之间; 1 - 如果长度在3到5之间; 2 - 如果长度在6到8之间; 3 - 如果长度为9+。然后根据查询长度,您只需检查给定类别的单词。此外,在超出max. distance时停止算法应该不难。
  3. 如果需要,可以考虑实施一些machine learning方法。

答案 2 :(得分:1)

编辑:新解决方案

是的,计算输入和单词列表之间的Levenshtein距离可能是一种合理的方法,但需要批次时间。 BK树可以改善这一点,但随着Levenshtein距离变大,它们会迅速变慢。似乎我们可以使用 trie 来加速Levenshtein距离计算,正如这篇优秀博客文章所述:

Fast and Easy Levenshtein distance using a Trie

它依赖于以下事实:Levenshtein距离的动态编程查找表在不同的调用中具有公共行,即levenshtein(kate,cat)levenshtein(kate,cats)

使用TWL06字典运行该页面上给出的Python程序,提供:

> python dict_lev.py HACKING 1
Read 178691 words into 395185 nodes
('BACKING', 1)
('HACKING', 0)
('HACKLING', 1)
('HANKING', 1)
('HARKING', 1)
('HAWKING', 1)
('HOCKING', 1)
('JACKING', 1)
('LACKING', 1)
('PACKING', 1)
('SACKING', 1)
('SHACKING', 1)
('RACKING', 1)
('TACKING', 1)
('THACKING', 1)
('WHACKING', 1)
('YACKING', 1)
Search took 0.0189998 s

这真的很快,在其他语言中会更快。大部分时间花在构建trie上,这是无关紧要的,因为它只需要完成一次并存储在内存中。

唯一的一个小缺点就是尝试占用大量内存(可以通过DAWG减少,但代价是速度)。

另一种方法:Peter Norvig在拼写纠正方面有一篇很棒的文章(包含完整的源代码)。

http://norvig.com/spell-correct.html

这个想法是建立可能的单词编辑,然后选择最可能的单词拼写纠正。