我昨天提出了这个问题(https://stackoverflow.com/questions/22992011/data-structure-for-efficent-time-and-space-searching-of-strings-with-substrings),但我意识到这种做法可能是错误的。
我需要的是一个数据结构,允许我存储字符串并检索其中包含常用单词的所有字符串。不幸的是,我不能等待用户输入所有单词,但我必须依赖前缀。
例如,如果我有字符串"这是结构内的一个字符串"和#34;二叉树数据结构"并输入一个前缀,如" stru"它们都必须在输出中。
有什么有用的方法吗?
答案 0 :(得分:1)
这在很大程度上取决于列表中有多少个字符串。如果您的列表不是 huge ,那么您可以在用户输入第一个字符时进行简单的顺序搜索,然后搜索该搜索的结果以进行后续搜索。
例如,考虑列表中是否有1,000个字符串,用户键入's'
。您搜索原始列表,最后得到包含该字母的623个单词的列表。拨打该列表S
。
然后,用户键入't'
,然后在S
搜索子字符串"st"
。调用结果ST
。
您将继续这种方式,进行连续搜索并削减列表。它看起来像这样:
// search in initial list of strings for first character
results = new list
for each string in list
if string.Contains("s")
Add string to results
// for subsequent characters the user types
newResults = new list
for each string in results
if string.Contains("st")
Add string to newResults
results = newResults
列表会很快变得非常小。如果您认为英语中最常见的二元组“th”出现在大约3.8%的单词中,很可能在输入两个字符之后,您的列表中的单项数量少于40个。顺序搜索将非常非常。向用户显示结果所需的时间比搜索列表所需的时间长。
即使您的初始字符串列表有100万个项目,用户也可能不会注意到键入的第一个字符的延迟。如果您逐步显示结果而不是在显示任何内容之前等待搜索整个列表,则尤其如此。
您可以通过创建第一个字符的索引来加快速度。也就是说,为字符串列表中包含的每个字符创建一个字典条目。它将是Dictionary<char, List<string>>
。因此,如果用户键入's'
,您将获得列表:
List<string> startingList = _dict['s'];
然后使用上一个算法,从该列表开始。
这可以加快您的初始搜索速度,但会占用大量内存。除非你有一个非常大的初始字符串列表,否则不值得做。
有关详细信息,请参阅Know when to stop optimizing。
如果你正在处理大量的字符串,你可能应该构建一个前缀树,但限制它的深度。例如,构建一个前缀树,其最大深度为3或4个字符,然后使用我在上面显示的顺序方法。这将占用比完整前缀树少得多的内存,并且应该表现得非常好。当你得到四个字符时,包含该子字符串的行数应该非常小。
您应该编写代码来分析数据中的n-gram计数。查看您拥有多少个不同的双字母以及与二元组匹配的最大行数。用三卦,4克等做同样的事情来确定你的截止点应该在哪里。例如,如果您发现4-gram没有超过1,000个匹配行,那么您可能应该将前缀树限制为四个级别,然后使用顺序技术。之后真的没有必要创建一个完整的前缀树。虽然您可以创建完整的前缀树,然后将hapaxes折叠为单个节点。你最终得到的本质上是一个有向无环的字图。
但请记住,您正在响应用户输入。因此节省几毫秒并不是那么重要。用户不会注意到20毫秒响应时间和5毫秒响应时间之间的差异。代码不必“尽可能快”,而是“足够快*为用户。如果你记住这一点,你可以创建一个更简单的解决方案,使用更少的内存。