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

String 如何从混乱的单词中找到单词,string,algorithm,data-structures,hash,tree,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) 我正在尝试的方法: 比如说,我有一个散列,其中包含从a

我试图找到一种方法,在混乱的文本中找到连续出现的特定单词。找不到的字符将有一个
X

例如,假设字典中的单词列表为:

jane
john
brownbag
foo
youth
和加扰文本:

ofozlhuoyt => fooXXyouth
yuawbnrobgajen => XXbrownbagjane
janjeohn => (nothing since jane and john aren't consecutive)
我正在尝试的方法:

比如说,我有一个散列,其中包含从
a
z
的键,每个键都设置为值。集合中的每个数字将表示包含特定字符的单词的索引

根据以上示例:

{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 => fooXXyouth
yuawbnrobgajen => XXbrownbagjane
janjeohn => (nothing since jane and john aren't consecutive)
第一个字符串:
Ofozhuoyt

  • o=>存在于1、2、3和4中

  • 从1开始:jane(长度4)

  • 获取4个字符:
    ofoz

  • “jane.sort(false)=“ofoz.sort(false)”

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

  • 如果为true:将foo添加到好单词列表中,并使用
    z开始步骤0


  • 有更好的方法吗?我觉得有一个更好的数据结构可以解决类似的问题,但我不知道该使用哪个。

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

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

    aejn
    aenj
    ajen
    ajne
    anej
    anje
    etc.
    
    然后,为构建一个状态机,使单个单词的每个排列进入相同的结束状态。该结束状态将输出您要查找的字符串

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

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


    我使用了一种改进的Aho-Corasick算法,该算法在视频标题中搜索数百万短语(乐队和歌曲名称)的文本。状态机占用了大约10G的RAM,并且花费了大约一个小时来构建,但是在匹配方面它的速度很快。

    你可以使用素数

    当你将n个素数相乘时,你得到的乘积将不同于任何其他的素数组合

    在您的问题中,关键是顺序并不重要,因此排序将是浪费时间。换句话说,

    'jane' == 'ejna' == 'jnea' == ...
    
    因此,您可以基于cool prime属性创建自己的哈希函数,并使用乘法上的交换性来避免排序/字符串搜索。在python中,您甚至不必担心int的大小;如果你的字典里有很多大词的话,那就派上用场了

    下面是一个简单的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。我提出了一个外部链接哈希表。让我们称之为“散字”

    # 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]
    
    运行后,散列的单词如下所示:

    {1113571: ['john'], 27434: ['jane'], 
     28717: ['foo'], 448956643: ['youth'], 
     3131090838L: ['brownbag'], 2967: ['nib', 'bin']}
    
    这就是我们想要的

    现在,您可以通过计算字母的乘积开始对加扰单词进行哈希运算,并在每个点检查乘积是否在哈希单词中。像其他人提出的那样的状态机对于像“mrtasgth”中的“mart”和“smart”这样的情况是必要的(见下面的评论)

    <>注意:不要按升序分配素数,可以考虑字典中出现的所有字母的频率分布,并将最低素数分配给最高频率的字母。这确实可以在创建“哈希单词”哈希表时节省内存。

    可能类似于:

    http://en.wikipedia.org/wiki/Rabin–Karp_算法


    这与hash思想非常相似,与aho-corasick算法相关

    非常好的方法,我不确定是否有可能从逻辑角度对其进行更多优化,可能只是在一开始就对字典中的单词进行排序(每次比较都没有意义),此外,如果您按单词长度按asc顺序存储词典ID,您可以最大限度地减少“ofoz”排序调用的数量。您可以使用状态转换表构建一个状态机,例如,
    j
    转到
    n
    (可能是jane或john)、
    ae
    (可能是jane)、
    oh
    (可能是john)或它们中的任何一个,诸如此类。我将如何着手建造它。听起来我需要一个树形结构。我同意@500 InternalServerError。在这里,状态机是正确的选择。不过,不要试图自己实现它。是。。。至少可以说是复杂的。我过去用过,非常喜欢。可以用Trie吗?一个有趣的想法。它适用于相对较小的文件。但是如果遇到一根更长的绳子,那将需要很长的时间。基本上,您按照
    n*longest\u string
    查找的顺序进行查找,其中
    n
    是文本长度,
    longest\u string
    是散列最大的字符串长度。此外,给定搜索词
    [“mart”,“smart”]
    ,您的算法将查找
    “mart”
    ,但在对字符串
    “armts”
    @JimMischel运行时,将找不到
    “smart”
    。您是对的,它将需要n*个最长的字符串查找。但对于“智能”和“智能”问题,一台状态机将完成这项工作。我更新了答案以反映这一点。状态机将是非确定性的,对吗?因为一个给定的模式可以生成多个单词。例如,
    owh
    可以转到
    who
    how
    @Chth