【问题标题】:Algorithm for multiple word matching in text文本中多词匹配的算法
【发布时间】:2010-11-09 03:45:31
【问题描述】:

我有大量的单词(大约 10,000 个),我需要找出其中是否有任何单词出现在给定的文本块中。

有没有比对文本块中的每个单词进行简单文本搜索更快的算法?

【问题讨论】:

    标签: algorithm search text


    【解决方案1】:

    将 10,000 个单词输入到哈希表中,然后检查文本块中的每个单词是否有条目。

    虽然我不知道更快,但只是另一种方法(取决于您要搜索的字数)。

    简单的 perl 示例:

    my $word_block = "the guy went afk after being popped by a brownrabbit";
    my %hash = ();
    my @words = split /\s/, $word_block;
    while(<DATA>) { chomp; $hash{$_} = 1; }
    foreach $word (@words)
    {
        print "found word: $word\n" if exists $hash{$word};
    }
    
    __DATA__
    afk
    lol
    brownrabbit
    popped
    garbage
    trash
    sitdown
    

    【讨论】:

    • 我打算推荐 KMP,但这正是所需的解决方案。 +1,应该得到答案检查。
    • 是的,这与您将获得的一样好...... O(N) 时间(当然假设一个好的哈希函数)
    • @Polaris:还假设单词的哈希值适合内存(如果是 10k 单词,应该没问题。只是迂腐)
    • 如果“给定的文本块”很小,并且对 10,000 个单词进行了排序,那么不打扰哈希表可能(可能)会更快,只需二进制印章(即使在换行符中 -分隔列表)。 O(M log N) 而不是 O(M+N)。但出于一般目的,它必须是这个哈希表,或者像 trie 这样的花哨的东西。
    • 为什么是散列而不是链表?
    【解决方案2】:

    【讨论】:

    • +1 因为那实际上应该解决我的真正问题(我说的是“单词”,但我应该说字符串,因为它们也可以是两个、三个或四个单词的字符串
    • Aho-Corasick 对于这么大的字符串集是否足够高效?我在 Java hkn.eecs.berkeley.edu/~dyoo/java/index.html 中找到了一个我可能会使用的实现。
    • 我相信它是,基于我在网上找到的所有相关文章。
    • 如果这解决了“真正的”问题,那么接受 Cuga 的回答。 Aho-Corasick 是一只优雅的野兽。由于搜索字典(要搜索的字符串集)中字符串中的空格,它在您的情况下特别有用。例如,使用 User105033 的方法(散列),您不能只检查每个单词;相反,您必须检查每个单词/和/每个连续的 2、3、4、... 单词等。相比之下,使用状态机方法,这是隐式完成的。
    【解决方案3】:

    建立一个trie 你的单词,然后用它来查找文本中的哪些单词。

    【讨论】:

      【解决方案4】:

      答案很大程度上取决于实际需求。

      1. 单词列表有多大?
      2. 文本块有多大?
      3. 必须处理多少个文本块?
      4. 每个文本块必须多久处理一次?
      5. 文本块或单词列表是否改变?如果,多久一次?

      假设文本块与单词列表相比相对较小并且每个文本块只处理一次,我建议将单词列表中的单词放入哈希表中。然后您可以对文本块中的每个单词执行哈希查找,并找出单词列表中是否包含该单词。

      如果您必须多次处理文本块,我建议反转文本块。反转文本块意味着为每个单词创建一个列表,其中包含包含特定单词的所有文本块。

      在其他情况下,为每个文本块生成一个位向量可能会有所帮助,每个单词一个位指示该单词是否包含在文本块中。

      【讨论】:

        【解决方案5】:

        您可以构建一个用作状态机的图形,当您处理输入单词的第 i 个字符 - Ci - 您尝试通过检查您的前一个节点是否链接到 Ci-1 来进入图形的第 i 个级别, 有一个子节点链接到 Ci

        例如:如果您的语料库中有以下单词
        (“艺术”、“是”、“是”、“蜜蜂”)
        您的图中将有以下节点
        n11 = 'a'
        n21 = 'r'
        n11.sons = (n21)
        n31 = 'e'
        n32='t'
        n21.sons = (n31, n32)
        n41='art' (这里我们的图中有一个叶子,并且所有上层节点的单词 build 都与这个节点相关联)
        n31.sons = (n41)
        n42 = 'are' (这里又是一个词)
        n32.sons = (n42)
        n12 = 'b'
        n22 = 'e'
        n12.sons = (n22)
        n33 = 'e'
        n34 = '是' (单词)
        n22.sons = (n33,n34)
        n43 = '蜜蜂' (单词)
        n33.sons = (n43)

        在处理过程中,如果您在处理输入单词的最后一个字符时穿过一片叶子,并且仅在这种情况下,这意味着您的输入在您的语料库中。

        这种方法比单个 Dictionary 或 Hashtable 实现起来更复杂,但在内存使用方面会更加优化

        【讨论】:

        • 根据 Cuga 的回答,(通常的)状态机方法称为 Aho-Corasick。
        【解决方案6】:

        Boyer-Moore string algorithm 应该可以工作。根据文本块中的大小/# 或单词,您可能希望将其用作搜索单词列表的关键字(列表中的单词是否比块中的单词多)。另外 - 您可能想从两个列表中删除任何重复。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-12-08
          • 2016-10-23
          • 2011-02-03
          • 1970-01-01
          • 2012-04-20
          相关资源
          最近更新 更多