【问题标题】:Fastest way to select all strings from list starting from从列表中选择所有字符串的最快方法
【发布时间】:2012-02-23 05:00:47
【问题描述】:

我正在寻找从一组字符开始查找集合中所有字符串的最快方法。我可以为此使用排序集合,但是我找不到在.net 中执行此操作的便捷方法。基本上我需要在符合条件的集合中找到低索引和高索引。

List 上的 BinarySearch 不保证返回的索引是第一个元素的索引,因此需要向上和向下迭代以查找所有匹配的字符串,如果列表很大,则速度不快。

还有 Linq 方法(并行),但我不确定哪种数据结构会提供最佳结果。

列表示例,约 10M 条记录:

aaaaaaaaaaaaaaabb
aaaaaaaaaaaaaaba
aaaaaaaaaaaaabc
...
zzzzzzzzzzzzzxx
zzzzzzzzzzzzzyzzz
zzzzzzzzzzzzzzzzzza

搜索从:skk...开始的字符串

结果:记录从 x 到 y 的索引。

更新:字符串可以有不同的长度并且是唯一的。

【问题讨论】:

    标签: c# .net linq algorithm


    【解决方案1】:

    就时间复杂度而言 - 您应该使用trie,而不是排序集或二分查找。

    Trie 将为您提供 O(|S|) 时间复杂度 [而排序集和二分搜索让您 O(|S|logn)] 找到代表该前缀的节点 [让它成为 v]。

    trie 中符合前缀的所有字符串 [paths] 将通过 v“传递”。通过向每个节点添加numberOfLeaves 字段,您可以准确地找出该节点有多少叶子[=strings]。

    在单次传递中 - 您还可以找到此 v 的索引 [对于从根到 v 的路径中的每个节点 u - 为每个兄弟节点求和 numberOfLeaves 到 @987654331 @]。

    与使用现有结构相比,这需要更多的工作,但如果数据量很大 - 它可以使您的算法大大更快,因此如果性能是一个问题并且您期望大量的字符串。

    【讨论】:

    • 哇,试试看——这看起来正是这里可能需要的。添加元素的时间不是问题,只需快速查找即可。谢谢!
    【解决方案2】:

    您可以通过手写的二分搜索来做到这一点——当找到匹配项时它不会停止;它会一直持续到找到一个索引为止。

    事实上,您甚至不必自己编写二进制搜索位 - 您可以创建一个自定义比较器,从不返回 0,即如果您正在寻找“abc”然后它将“abb”视为低于目标值,但将“abc”视为高于目标值。这样BinarySearch总是返回一个负数,然后您可以通过位翻转找到“位于 abb 和 abc 之间的字符串”的理论插入点。

    您可以反过来做同样的事情(将“abc”视为低于目标值)以找到最高界限。

    如果您知道这些字符串的格式,并且不会出现像 Unicode NULL 字符这样的边缘情况,并且所有内容的长度都相同,那么您甚至可以在不编写自己的比较器的情况下做到这一点:

    // This could be done more efficiently :)
    string stringJustBelow = target.Substring(0, target.Length - 1) +
                             target[target.Length - 1] + "X";
    string stringJustAbove = target + "X"; // Or any character
    
    int lowerBoundInclusive = ~list.BinarySearch(stringJustBelow);
    int upperBoundExclusive = ~list.BinarySearch(stringJustAbove);
    

    因此,如果您的字符串长度均为 3,并且您正在搜索“abc”,那么您实际上会寻找“abbX”和“abcX”的插入位置。

    【讨论】:

      【解决方案3】:

      将它们放入SortedSet 并使用GetViewBetween

      This answer 说明了同时搜索前缀和后缀,如果这确实是您想要的,我相信您可以轻松地将其调整为仅前缀搜索。

      如果你只是想搜索一个范围(不是前缀),直接使用GetViewBetween就足够了。

      【讨论】:

      • +1 请注意,排序后的集合不允许重复字符串,因此如果原始列表包含重复项,则需要做更多工作(例如,使用SortedDictionary<string, int>,其中整数是重复项数数)。另请注意,将字符串保存在 SortedSet(或 SortedDictionary)而不是列表中将避免在大型对象堆上分配大型数组,这可以提高应用程序的内存使用率。
      猜你喜欢
      • 2022-09-30
      • 2010-10-26
      • 1970-01-01
      • 2012-02-12
      • 2018-06-24
      • 2015-10-01
      • 2014-06-24
      • 1970-01-01
      相关资源
      最近更新 更多