如何从混乱的单词中找到单词

时间:2013-11-12 23:53:21

标签: string algorithm data-structures hash tree

我正试图找到一种方法来查找连续出现的加扰文本中的特定单词。未找到的字符将具有X

例如,假设词典单词列表是:

jane
john
brownbag
foo
youth

和乱序文字:

ofozlhuoyt => fooXXyouth
yuawbnrobgajen => XXbrownbagjane
janjeohn => (nothing since jane and john aren't consecutive)

方法我正在尝试:

说,我有一个密钥az的哈希值,并设置为每个密钥的值。集合中的每个数字将表示包含特定字符的单词的索引。

从上面的例子:

{a: [0,2]}
{b: [2]}
{c: []}
{e: [0]}
{f: [3]}
{g: [2]}
{h: [1,4]}
{j: [0,1]}
...
{n: [0,1,2]}
{o: [1,2,3,4]}
{r: [2]}
{u: [4]}
{t: [4]}
{w: [2]}
{y: [4]}
...
{z: []} 

在准备好上述内容后,我们可以开始查看加扰文本的每个字符:

第一个字符串:ofozlhuoyt

  1. o =>存在于1,2,3和4

  2. 从1开始:jane(长度4)

  3. 获得4个字符:ofoz

  4. "jane".sort(false) == "ofoz".sort(false)?

  5. 如果为false:对2(john)重复步骤1到3

  6. 如果为true:将foo添加到好词列表中,并使用z

  7. 开始第0步

    有更好的方法吗?我觉得有一个更好的数据结构可以解决这样的问题,但我无法弄清楚要使用哪个..

3 个答案:

答案 0 :(得分:2)

如果你有足够的内存来实现它,有一种可能更快的方法。

首先,为每个单词生成所有排列。所以对于“简”,你会有:

aejn
aenj
ajen
ajne
anej
anje
etc.

然后,为Aho-Corasick algorithm构建状态机,单个单词的每个排列都进入相同的结束状态。结束状态将输出您正在寻找的字符串。

现在通过状态机运行文本。输出将是找到的单词及其位置。然后,您可以按位置对找到的单词进行排序,并确定它们是否连续出现。

状态机可能非常大(每个单词的n!状态,其中n是单词中的字符数),并且需要一些时间来构建。但是一旦它被构建,它就会很快匹配。如果你的单词列表是静态的,并且你有很多要搜索的文本,那么这就是你要走的路。只要你有足够的记忆。

我使用了一种改进的Aho-Corasick算法,该算法在视频标题中搜索文本中出现的数百万个短语(乐队和歌曲名称)。状态机占用大约10千兆字节的内存并花费了大约一个小时来构建,但是在匹配时它是 fast

答案 1 :(得分:2)

你可以使用素数!

当您乘以n素数时,您获得的产品将与其他任何组合的素数不同。

在您的问题中,关键是订单无关紧要,因此排序将浪费时间。换句话说,

'jane' == 'ejna' == 'jnea' == ...

因此,您可以基于cool prime属性创建自己的哈希函数,并使用乘法的交换来避免排序/字符串搜索。在python中,你甚至不必担心整数的大小;如果你的词典里面有很大的词汇,这将会派上用场。

下面是一个简单的dict映射字母到前26个素数,以及随附的散列函数。

letters_to_primes = {'a': 2, 'b': 3, 'c': 5, 'd': 7, ... 'x': 89, 'y': 97, 'z': 101}

def my_prime_hash(word):
    sum = 1
    for letter in word:
        sum = sum * letters_to_primes[letter] # Multiplication is commutative!
    return sum

同样,我们在这里利用的关键属性是

my_prime_hash('jane') == my_prime_hash('enaj') == ... == 27434

现在我们只需要创建给定字典单词的dict。我建议使用外部链接哈希表。我们称之为'hashed_words'。

# Given these words
words = ['jane', 'john', 'brownbag', 'foo', 'youth', 'nib', 'bin']

# Compute the hash table
hashed_words = {}
for word in words:
    w_hash = my_prime_hash(word)
    if w_hash in hashed_words: hashed_words[w_hash].append(word)
    else: hashed_words[w_hash] = [word]

运行之后,hashed_words看起来像:

{1113571: ['john'], 27434: ['jane'], 
 28717: ['foo'], 448956643: ['youth'], 
 3131090838L: ['brownbag'], 2967: ['nib', 'bin']}

这就是我们想要的。

现在,您可以通过计算字母的产品来开始对乱码字进行散列,并在每个点检查产品是否为hashed_words。类似于其他人提出的状态机对于“mrtasgth”这个混乱的单词中的“mart”和“smart”等情况是必要的(见下面的评论)。

注意:您可以考虑字典中出现的所有字母的频率分布,并将最低素数分配给频率最高的字母,而不是按升序分配素数。这确实会在创建'hashed_words'哈希表时节省内存。

答案 2 :(得分:1)

可能是这样的:

http://en.wikipedia.org/wiki/Rabin-Karp_algorithm

与哈希思想非常相似,与aho-corasick算法相关