从列表中选择所有字符串的最快方法

时间:2012-02-22 20:17:42

标签: c# .net linq algorithm

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

列表上的二进制搜索< T>不保证返回的索引是第一个元素的索引,因此需要上下迭代以查找所有匹配的字符串,如果一个字符串很大,则不会很快。

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

列出示例,约10M的记录:

aaaaaaaaaaaaaaabb
aaaaaaaaaaaaaaba
aaaaaaaaaaaaabc
...
zzzzzzzzzzzzzxx
zzzzzzzzzzzzzyzzz
zzzzzzzzzzzzzzzzzza

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

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

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

3 个答案:

答案 0 :(得分:4)

你可以用手写二进制搜索来做到这一点 - 当它找到匹配时不会停止;它一直持续到找到一个索引。

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

您可以反向执行相同操作(将“abc”视为 lower 而不是目标值)以找到最高限制。

如果您知道这些字符串的格式并且它不会有像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”的位置。

答案 1 :(得分:4)

就时间复杂度而言 - 您应该使用trie,而不是排序集或二进制搜索。

Trie会给你一个O(|S|)时间复杂度[虽然有序集合和二进制搜索让你O(|S|logn)]找到代表该前缀的节点[让它为v]。 / p>

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

在一次传递中 - 您还可以找到此v的索引[对于从根到u - 总和v的路径中的每个节点numberOfLeaves每个兄弟都留给u]。

这需要更多的工作,然后使用已有的结构,但如果数据量巨大 - 它可以使您的算法更多更快,所以如果性能是一个问题并且您期望巨大的字符串。

答案 2 :(得分:2)

将它们放入SortedSet并使用GetViewBetween

This answer说明了搜索前缀和后缀,我确信你可以毫无困难地将其改编为仅前缀搜索,如果这确实是你想要的。

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