【问题标题】:Substring search on HashMap ValuesHashMap 值的子字符串搜索
【发布时间】:2016-10-30 03:07:31
【问题描述】:

给定一个 HashMap,我想检索其值包含给定子字符串 s(不区分大小写)的所有条目 e。我正在寻找仅适用于前缀/后缀匹配的后缀树 (trie) 行上的子字符串索引想法。

【问题讨论】:

  • 您在使用 Java 吗?子串键的集合是已知的还是动态的?
  • 我不认为没有迭代你就可以做到。
  • @TimBiegeleisen 我正在使用 C#。是的,键是动态的。
  • @AbdullahTellioglu - 是的,我目前正在做一个效率低下的完整迭代。
  • @AswinSivaN 如果您不必使用 hashmap ,请尝试使用搜索树。它的平均登录复杂度是好的

标签: c# string algorithm hashmap suffix-tree


【解决方案1】:

基于广义后缀树的解决方案

后缀树不仅适用于后缀匹配。以下是您可以执行的操作:

  • 为哈希表中的每个条目构建一个通用后缀树。请注意,您必须将所有字符串转换为任意大小写才能忽略大小写。在构造过程中,用共享它的字符串集标记每个叶子(例如字符串hazelnutcoconut将共享代表nututt的叶子)
  • 从根开始:
    • 使用子字符串 s 沿着树向下走(转换为第一步中选择的情况):您最终处于隐式状态(即处于边缘中间)或显式状态(你最终进入一个节点 N )。
    • 如果您处于隐式状态,只需获取您所在边缘的目标节点,我们称该节点为 N
  • 计算你可以从N到达的所有叶子的字符串集合的并集:你得到一组字符串S
  • S 是哈希表中具有子字符串 s 的所有字符串的集合

复杂性分析

K 为表中的字符串数。令 Li 为字符串 Si 的各自长度,令 L = ∑ L我。树的构造将是O(L)

走到节点NO(length(s))

现在更棘手的部分开始了。列出一个节点可到达的所有叶子不是线性的,但不会太麻烦。

Lmax = max(Li),那么你可以步行最多到达每一片叶子> Lmax 个节点,更准确地说,如果你从之前定义的 N 个节点开始,你会到达 的每个子叶子N 最多 L(s) = Lmax - 长度(s) 步。

N开始的子树也具有广义后缀树的结构。它表示最多 K 个长度最多为 L(s) 的字符串。这些字符串中的任何一个都最多有 L(s) 个叶子。所以迭代它们最多是 O(K.L(s)2).

在每个这样的叶子中计算字符串集合的并集最多O([K.L(s)]2)。 (实际上它会更接近于 O(KL(s)2) 因为如果每个叶子都在其所有原始 K 字符串集,则以 N 为根的子树中只有 L(s) 个叶子)。

这导致最坏情况的总复杂度为:

O(L + 长度(s) + [K.L(s)]2)

但实际使用复杂度将更接近:

O(L + length(s) + K.[L(s)]2)

标准方法(遍历每个字符串并在每个字符串中搜索s)是:

O(∑(Li + length(s))) = O(L + K.length(s))

但是等等……我们总是在寻找子字符串s!所以 KMP 算法 的预处理可以只进行一次......这降低了这种方法的复杂性:

O(L + 长度)

但是,为了从这种优化中受益,您必须自己编写而不是使用标准实现...

结论

假设您只需要在地图上测试一个字符串 s,那么简单的解决方案易于实现、易于理解,并且其整体复杂性不仅 优于基于后缀树的方法,它是最佳的。所以你可以自信地坚持下去。

但是,如果你必须测试大量 Ks 个字符串 sj,那么后缀树方法可能会更好,因为它的整体复杂度最多为:

O(L + Ks.(max(length(sj)) + [K.max(L(sj)]2))

而 KMP 方法将导致以下总体复杂性:

O(Ks.L + ∑(长度(sj))) = O(Ks.[ L + max(长度(sj))])

还请注意,由于后缀树是一种树状结构,如果设计不当,内存访问和分配就会发挥作用,严重影响运行时间。


如果你愿意,我可以举一个例子(在 C++ 中,但它仍然可以说明问题)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-05
    • 1970-01-01
    • 1970-01-01
    • 2019-10-06
    • 2017-02-09
    • 2011-09-25
    • 2011-02-10
    相关资源
    最近更新 更多