我正在寻找从一组字符开始查找集合中所有字符串的最快方法。我可以使用排序集合,但是我找不到在.net中执行此操作的便捷方法。基本上我需要在符合条件的集合中找到低和高索引。
列表上的二进制搜索< T>不保证返回的索引是第一个元素的索引,因此需要上下迭代以查找所有匹配的字符串,如果一个字符串很大,则不会很快。
还有Linq方法(并行),但我不确定哪种数据结构会提供最佳效果。
列出示例,约10M的记录:
aaaaaaaaaaaaaaabb
aaaaaaaaaaaaaaba
aaaaaaaaaaaaabc
...
zzzzzzzzzzzzzxx
zzzzzzzzzzzzzyzzz
zzzzzzzzzzzzzzzzzza
搜索从以下字符开始的字符串:skk ...
结果:记录从x到y的索引。
更新:字符串可以有不同的长度并且是唯一的。
答案 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
就足够了。