针对5亿个字符串进行前缀搜索

时间:2017-01-15 17:43:57

标签: python string algorithm search data-structures

我有一个500密耳的字符串列表。字符串是字母数字,ASCII字符,大小不一(通常为2-30个字符)。此外,他们还是单个单词(或没有空格的单词组合,例如' helloiamastring')。

我需要的是一种快速检查目标的方法,比如说' hi'。结果应该是500mil列表中的所有字符串,以' hi'开头。 (例如,' hithere',' hihowareyou'等)。这需要很快,因为每次用户输入内容时都会有一个新的查询,所以如果他输入" hi",所有字符串都以" hi"开头。将显示500密耳列表,如果他键入"嘿",所有字符串以&#34开头;嘿"将展示等。

我尝试过Tries算法,但存储300 mil字符串的内存占用空间非常大。它应该需要我100GB + ram。而且我非常确定该名单将增长到十亿。

此用例的快速算法是什么?

P.S。如果没有快速选项,最好的选择是限制人们在结果显示之前至少输入4个字符。有没有快速的方法来检索结果呢?

6 个答案:

答案 0 :(得分:3)

您需要Directed Acyclic Word Graph或DAWG。这概括了@ greybeard建议使用词干。

例如,参见this的第3.2节中的讨论。

答案 1 :(得分:2)

如果字符串已排序,那么二进制搜索是合理的。作为加速,您可以维护所有可能的双字母组的字典(" aa"," ab"等),其中相应的值是以该双字母组开头的第一个和最后一个索引(如果有的话,那么在O(1)中,在一个包含你正在寻找的字符串的小得多的子列表中为零。找到匹配项后,请在右侧和左侧进行线性搜索以获得所有其他匹配项。

答案 2 :(得分:0)

例如,如果你想强迫用户数字至少4个字母,你可以保留键值映射,内存或磁盘,其中键都是4个字母的组合(如果它们不是太多的话)不区分大小写,否则您可以限制为3),并且值是以组合开头的所有字符串的位置列表。

用户输入三个(或四个)字母后,您可以立即获得所有可能的字符串。从这一点开始,您只需循环使用此子集。

平均而言,这个子集足够小,即500M除以26 ^ 4 ......就像一样。实际上更大,因为可能不是所有4个字母组都可以作为你的字符串的前缀。

忘了说:当您向新列表添加新字符串时,还会更新与地图中的键对应的索引列表。

答案 3 :(得分:0)

如果您不想使用某个数据库,则应在所有数据库引擎中预先存在一些与数据相关的例程:

  1. 不会尝试在内存中加载所有数据。
  2. 对所有字符串使用固定长度。它增加了存储内存消耗但显着减少了搜索时间(第i个字符串可以在文件中的位置L * i字节处找到,其中L - 固定长度)。创建额外的机制来处理非常长的字符串:将它存储在不同的地方并使用特殊指针。
  3. 对所有字符串进行排序。您可以使用合并排序来执行此操作,而无需一次加载内存中的所有字符串。
  4. 创建索引(第一行的地址以'a','b'开头......)也可以为2克,3克等创建索引。索引可以放在内存中以提高搜索速度
  5. 使用高级策略避免数据更新时完全索引重新生成:通过首字母将数据拆分为多个文件并仅更新受影响的索引,在数据中创建空格以减少读取 - 修改 - 写入过程的影响,创建新行之前的缓存,然后将它们添加到主存储并在此缓存中搜索。
  6. 使用查询缓存快速处理常用请求。

答案 4 :(得分:0)

在这个假设中,被索引的字符串与任何其他信息(例如同一行中的其他列)没有关联,完整索引与保持字符串排序在第一位之间的差异相对较小(如,一些差异,但没有你希望的那么多)。鉴于列表的不断增长的性质和更新它的成本,也许相反的方法将更好地完成您正在寻找的性能权衡。

对于字符串中任何给定位置的任何给定字符,您的基本情况是不存在包含该字母的字符串。例如,一旦“你好”#39;已键入,如果输入的下一个字母是' t',那么你的基本情况是没有字符串开头' hellot'。有一些人物可以跟随“你好”。在第5位(比方说26)。你需要26个固定长度的空间来存储关于以下字符的信息。你好'如果没有字符串,每个空格要么为零,例如,' t'跟随' hello'或者包含许多数据存储地址,通过这些地址可以提前找到一个或多个字符串涉及该字符跟随' hellot'在位置6(或使用绝对数据存储地址,尽管只有相对地址允许我建议的算法支持无限长度的无限长度的字符串而不进行任何修改,以便在列表增长时允许更大的指针。)

然后,算法可以向前移动存储在磁盘上的数据,在内存中构建一个字符串开头的树,并避免随机访问读取引起的延迟。对于内存中索引,只需将最靠近根的树部分存储在内存中。在用户输入“你好”后#39;并且该算法已经跟踪了关于一个或多个字符串的信息,这些字符串开始于' hellot'在数据存储地址X处存在该算法,该算法在位置X处找到两种类型的列表中的一种。要么它是另一个序列,例如26个固定长度的空间,其中包含关于字符' hellot'之后的字符的信息。在位置6,或者它是一个预先分配的空间块,列出了跟随' hellot'之后的所有后修复,具体取决于存在多少这样的后修复。一旦有足够的后期修复,使用一些传统的搜索和/或排序算法来更新和搜索后修复列表无法提供您想要的性能优势,它将被分割并替换为例如, 26个固定长度的空间。

这涉及预先分配相对大量的磁盘存储,权衡您的树可以按照排序的形式维护,而无需为大多数更新移动任何内容,并且您的搜索可以在中进行搜索单个顺序读取中的完整。与基于将字符串本身存储为固定长度字符串的解决方案相比,它还提供了更大的灵活性,并且可能需要更少的存储空间。

答案 5 :(得分:0)

首先,我应该说你应该为你的问题添加的标签是"信息检索"。

我认为使用Apache Lucene' PrefixQuery是处理通配符查询的最佳方式。如果你对python感到满意,Apache有一个Python version。但是要使用Apache lucent来解决您的问题,您首先应该了解indexing您的数据(这是您的数据将以更有效的方式压缩和保存的部分)。

同时寻找IR book的索引和通配符查询部分将为您提供更好的愿景。