【问题标题】:Algorithm Question On Finding All Valid Words In Dictionary在字典中查找所有有效单词的算法问题
【发布时间】:2023-03-22 03:05:01
【问题描述】:

给定一个字典(只是一个字符串列表)。

您收到来自外部来源的未知数量的信件的提要。给定一串字母,你将如何列出所有有效的单词(来自字典),你可以从这些字母的任意组合中组成。

所以如果你收到:abpplead

你应该找到apple、bad、pad、lead等

我知道没有最佳答案。但是有哪些相当有效的方法可以做到这一点,使用什么数据结构等等。

此外,假设您可以对输入进行预处理,因此您可以选择将输入字母存储在您想要的任何数据结构中。

【问题讨论】:

标签: java algorithm


【解决方案1】:

将字典放入 trie 中。然后将字母放入一个按其字符值索引的计数器数组中,维护每个字母的计数(我们称之为 counts[])。然后以深度优先搜索顺序遍历 trie,在向下遍历时递减每个字母的 counts[letter] 值,并在返回时递增。现在,任何时候 counts[letter] 即将变为负数,停止并向上遍历。任何时候到达单词终止符,输出结果。

【讨论】:

    【解决方案2】:

    如果您不允许对该字符串列表执行任何预处理,那么就没有“合理有效的解决方案”:您将不得不遍历整个列表,检查每个单词是否可以根据需要组成(即,它的签名,见下文,一致地小于传入束的签名)。列表中 N 个字符串的 O(N)。

    如果允许预处理(您对列表进行一次预处理,然后回答几个查询,足以分摊预处理成本),然后为列表中的每个单词制作一个“签名”,这是一个由 26 个整数组成的数组,计算出现次数字符串中每个字母的名称(假设它是英文且不区分大小写——扩展很明显)。随着您的进行,构建从每个签名到具有该签名的单词列表的映射。对于 HashMap,这种预处理大约是 O(N)。

    现在,给定一堆 K 字母,您可以计算这串字母的签名并在地图中查找每个带有该签名的单词列表;对所有均匀较少的签名重复(O(2^K) 是这里的上限)。因此,对于 Z 这样的查找,您需要支付 O(N + Z * 2^K)(与 O(Z * N) 相比,无需预处理),因此如果 N > 2,您可以获得(对于足够大的数字,以便 O() 估计有意义) ^K.

    【讨论】:

      【解决方案3】:

      对于字典中的每个单词,检查它是否来自您只有的字母。
      要检查这一点,您可以创建像 dict x[letter: amount] 这样的辅助结构,用给定字母的数量对其进行初始化,然后:

      for each word 'w' in dictionary
          init x from given letters
      
          for each letter `ch` in word `w` 
              --x[ch]
              if x[ch] < 0
                  break, do not add w to result
      
          result.add(w)
      

      【讨论】:

      • 简单的蛮力算法。我在python中使用过类似的情况:半秒内搜索了一个355000字的字典。
      【解决方案4】:

      1.为字典中的每个单词创建一个树形结构。每个字母上的树枝,例如树的第一层是字母 a-z,如果有任何单词使用该组合,则下一层是 a-z,等等。树的叶子是单词。

      然后,当您获得字母组合时,只需从第一个字母的所有选项开始,沿着该分支向下移动树,然后搜索第二个字母的所有选项,等等。

      虽然这似乎是指数级的,因为并非所有的组合都是可能的,但您会发现无效的分支很快就会被修剪掉。

      【讨论】:

      • 这被称为特里。当它们是唯一可能的选择时,改进的版本将同一节点中的许多字母连接起来,这称为基数树。
      • 作为一个为基于规则的系统实现了Rete算法的人,我可以肯定地说这不是解决这个问题的合适算法。 Rete 在工作内存很大并且只有很小的增量更改并且需要匹配很多模式的情况下工作得很好。在这个问题中,工作记忆非常小(基本上是 1 个单词),并且随着每次匹配迭代而完全改变。
      【解决方案5】:

      将字典预处理为dawg,然后使用其中一种 dawg-walking 算法搜索子字符串。我有一些基本的 ruby​​ 代码用于处理 dawg here;它在实践中被证明太慢了,所以我转向了 ocaml(未发布),但它应该让您了解如何找到字谜。对于子字谜,即使您的包不是空的,也只需为单词结尾添加额外的检查。

      【讨论】:

        【解决方案6】:

        使用rete algorithm 并将问题视为基于规则的域中的问题。单词是规则(以它们自己的字母作为规则模式)。对于给定的每组字母,您应用规则库,所有匹配的单词都会“触发”。冲洗并重复。 :)

        [编辑 p.s.]

        这里的动机是这样的:“理论上,Rete 性能与系统中规则的数量[在您的情况下的字典中的单词] 无关”。

        【讨论】:

        • 作为一个已经为基于规则的系统实现了Rete算法的人,我可以肯定地说这不是解决这个问题的合适算法。 Rete 在工作内存很大并且只有很小的增量更改并且需要匹配很多模式的情况下工作得很好。在这个问题中,工作记忆非常小(基本上是 1 个单词),并且随着每次匹配迭代而完全改变。
        猜你喜欢
        • 1970-01-01
        • 2011-08-30
        • 1970-01-01
        • 1970-01-01
        • 2020-09-24
        • 1970-01-01
        • 2021-07-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多