什么是最快的子串搜索算法?


165

好的,所以我听起来不像个白痴,我将更明确地说明问题/要求:

  • 针(模式)和干草堆(要搜索的文本)都是C样式的以null终止的字符串。没有提供长度信息;如果需要,必须对其进行计算。
  • 函数应该返回指向第一个匹配项的指针,或者NULL如果找不到匹配项。
  • 失败案例是不允许的。这意味着具有非恒定(或大常数)存储要求的任何算法都将需要具有备用失败分配的备用情况(备用备用的性能因此会导致最差情况的性能)。
  • 尽管也可以很好地描述不带代码的算法(或指向该算法的链接),但是实现应使用C语言。

...以及“最快”的意思:

  • 确定性O(n)其中n=干草堆长度。(但是,O(nm)如果将它们与更鲁棒的算法结合使用以给出确定性的O(n)结果,则可以使用通常是正常的算法(例如滚动哈希)中的思想)。
  • 永远不要表现出if (!needle[1])比朴素的蛮力算法更糟的性能(可测量;几个时钟等等都可以),特别是在很短的针上,这可能是最常见的情况。(无条件的大量预处理开销是不好的,因为试图以可能的针头为代价来提高病理性针头的线性系数)。
  • 与任意其他广泛使用的算法相比,给定任意的针头和干草堆,其性能可比或更高(搜索时间长于50%以上)。
  • 除了这些条件,我将保留“最快”开放式的定义。一个好的答案应该可以解释为什么您考虑您建议的“最快”方法。

我的当前实现比glibc的双向实现大约慢10%至快8倍(取决于输入)。

更新:我当前的最佳算法如下:

  • 对于长度为1的针,请使用strchr
  • 对于长度为2-4的针,请使用机器字一次比较2-4个字节,如下所示:将针以16位或32位整数进行预加载并进行位移位,并在每次迭代时从干草堆中循环出旧字节/新字节。干草堆的每个字节仅被读取一次,并检查0(字符串末尾)和一个16位或32位比较。
  • 对于长度> 4的针,请使用双向算法以及一个错误的移位表(例如Boyer-Moore),该表仅应用于窗口的最后一个字节。为了避免初始化1kb表的开销(这对于许多中等长度的针头来说都是净损失),我保留了一个位数组(32字节)来标记移位表中的哪些条目已初始化。未设置的位对应于从未出现在机针中的字节值,因此可能会发生全针长移位。

我想到的主要问题是:

  • 有没有一种方法可以更好地利用不良转移表?Boyer-Moore通过向后扫描(从右到左)来最好地利用它,但是双向方法需要从左到右扫描。
  • 对于一般情况(没有内存不足或二次性能条件),我发现的仅有的两种可行的候选算法是有序字母表上的双向匹配字符串匹配。但是是否存在容易检测到的情况,其中不同的算法将是最佳选择?当然,空间算法中的许多O(m)m针长在哪里)都可以使用m<100。如果对可证明只需要线性时间的针进行简单的测试,也可以使用最坏情况的二次方算法。

奖励积分:

  • 您可以假设针和干草堆都是格式正确的UTF-8来提高性能吗?(具有变化的字节长度的字符,良好的格式会在针和干草堆之间提出一些字符串对齐要求,并在遇到不匹配的头字节时允许自动2-4字节移位。但是这些约束是否可以为您带来很多/其他好处?最大的后缀计算,良好的后缀移位等已经为您提供了各种算法?)

注意:我很清楚那里的大多数算法,只是他们在实践中表现不佳。这是一个很好的参考,所以人们不会一直给我关于算法的参考作为评论/答案:http : //www-igm.univ-mlv.fr/~lecroq/string/index.html


有相当多的上市字符串搜索算法的字符串算法。您可能需要描述从此列表考虑的算法。
Greg Hewgill'7

61
最后的那个链接是金!
卡洛斯(Carlos)2010年

4
我不敢相信您仍未接受答案。
user541686

1
@Mehrdad:我要说的是没有任何答案能够真正解决所问的问题,但您的答案似乎是正确的。在您回答时,我继续前进,并做了进一步的改进,strstr以备后用,因此我实际上还没有认真阅读您链接的论文,但这听起来确实很有希望。谢谢,很抱歉没有回复您。
R .. GitHub停止帮助ICE

Answers:


37

建立一个可能包含针叶草和干草堆的测试库。将测试描述为几种搜索算法,包括蛮力。选择最适合您数据的数据。

Boyer-Moore使用错误的字符表和良好的后缀表。

Boyer-Moore-Horspool使用错误的字符表。

Knuth-Morris-Pratt使用部分匹配表。

Rabin-Karp使用运行哈希。

它们都以开销为代价以不同程度地减少比较,因此,现实世界的性能将取决于针和干草堆的平均长度。初始开销越多,输入越长越好。如果针头很短,蛮力可能会赢。

编辑:

查找基对,英语短语或单个单词的最佳算法可能是不同的算法。如果所有输入都有一个最佳算法,那么它将被公开。

考虑下面的小桌子。每个问号可能都有不同的最佳搜索算法。

                 short needle     long needle
short haystack         ?               ?
long haystack          ?               ?

这实际上应该是一个图形,每个轴上的输入范围从短到长。如果在这样的图形上绘制每种算法,则每种算法将具有不同的签名。一些算法的模式重复很多,这可能会影响诸如搜索基因的用途。其他影响整体性能的其他因素是不止一次搜索同一模式,同时搜索不同模式。

如果我需要一个样本集,我想我会刮一个像google或wikipedia这样的网站,然后从所有结果页面中删除html。对于搜索站点,输入单词,然后使用建议的搜索短语之一。选择几种不同的语言(如果适用)。使用网页时,所有文本都会简短到中等,因此请合并足够的页面以获取更长的文本。您还可以找到公共领域的书籍,法律记录和其他大型文本。或者只是通过从字典中挑选单词来生成随机内容。但是,配置文件的目的是针对要搜索的内容类型进行测试,因此,请尽可能使用实际示例。

我留下了短暂而又模糊的时间。对于针,我认为short小于8个字符,medium小于64个字符,long小于1k。对于大海捞针,我认为short小于2 ^ 10,medium小于2 ^ 20,最长为2 ^ 30。


1
您对测试库有很好的建议吗?我在SO上提出的上一个问题与此相关,但我从未得到任何真正的答案。(除了我自己的...)应该广泛。即使我对strstr应用程序的想法是搜索英文文本,也可能有人在搜索碱基对序列中的基因……
R .. GitHub停止帮助ICE 2010年

3
它比长/短要复杂一些。对于针来说,与大多数算法的性能有关的主要问题是:长度?有周期性吗?机针是否包含所有唯一字符(无重复)?还是所有相同的角色?大海捞针中是否存在大量从未出现过的字符?是否有可能不得不应对想要利用最坏情况的性能来削弱系统性能的攻击者提供的保护措施?等等
R .. GitHub停止帮助ICE,2010年

31

我认为该算法很可能是Dany Breslauer,Roberto Grossi和Filippo Mignosi编写的“简单实时常量空间字符串匹配”算法,于2011年发布。

更新:

2014年,作者发表了以下改进:走向最佳打包字符串匹配


1
哇谢谢。我正在读报纸。如果结果比我的要好,我一定会接受您的回答。
R .. GitHub STOP HELPING ICE 2013年

1
@R ..:当然!:)说到这,如果您设法实现该算法,请考虑将其发布在StackOverflow上,这样每个人都可以从中受益!我在任何地方都没有找到任何实现,而且我也不擅长实现研究论文中发现的算法哈哈。
user541686 2013年

2
这是我已经在使用的“双向”算法的一种变体,因此修改我的代码以使用它实际上可能很容易。不过,我必须更详细地阅读本文,以确保确定,并且我需要评估所做的更改是否与我使用“不良字符表”兼容,从而大大加快了常见情况。
R .. GitHub停止帮助ICE,2013年

11
而且您仍然没有接受@Mehrdad的回答!:-)
生命平衡

3
@DavidWallace:什么?它具有论文标题和作者。即使链接失效,您也可以找到论文。您希望我做什么,为算法编写伪代码?是什么让您认为我了解算法?
user541686 '16

23

您指向的http://www-igm.univ-mlv.fr/~lecroq/string/index.html链接是一些著名和研究过的字符串匹配算法的绝​​佳来源和总结。

解决大多数搜索问题的方法包括在预处理开销,时间和空间要求方面进行权衡。在所有情况下,没有一个单一的算法会是最佳或实用的。

如果您的目标是设计一种用于字符串搜索的特定算法,那么请忽略我要说的其余内容;如果您想开发一个通用的字符串搜索服务例程,请尝试以下操作:

花一些时间来回顾您已经引用的算法的优点和缺点。进行审查的目的是找到一组涵盖您感兴趣的字符串搜索范围和范围的算法。然后,基于分类器功能构建一个前端搜索选择器,以针对给定输入的最佳算法为目标。这样,您可以采用最高效的算法来完成这项工作。当算法对于某些搜索而言非常好,但降级效果很差时,这特别有效。例如,蛮力可能是长度为1的针的最佳选择,但随着针长的增加而迅速退化,于是,司迪克·摩尔算法可能会变得更有效率(使用小字母),然后对于更长的针头和更大的字母,KMP或Boyer-Moore算法可能会更好。这些仅是说明可能策略的示例。

多重算法方法不是一个新想法。我相信它已经被一些商业的Sort / Search软件包采用(例如,大型机上常用的SYNCSORT实现了几种排序算法,并使用启发式方法为给定的输入选择了“最佳”算法)

每种搜索算法都有几种变体,它们可能会对其性能产生重大影响,例如,如本文所示。

对您的服务进行基准测试,以对需要其他搜索策略的区域进行分类,或者更有效地调整选择器功能。这种方法不是快速或容易的,但是如果做得好的话会产生非常好的结果。


1
感谢您的回复,尤其是我之前从未见过的Sustik-Moore链接。无疑,多种算法方法已被广泛使用。Glibc基本上根据strnee_len是1,<32还是> 32来执行strchr,不带错误字符移位表的双向或带不正确字符移位表的双向。除了始终使用移位表外,我目前的方法是相同的。我用一个用于标记表中哪些元素已初始化的位集上的32字节内存集替换了这样做所需的1kb内存集,即使是细小的针头,我也能获得好处(但不会产生开销)。
R .. GitHub停止帮助ICE,2010年

1
考虑了一下之后,我真的很好奇Sustik-Moore的预期应用是什么。使用小字母,您将永远不会发生任何重大变化(字母的所有字符几乎肯定会出现在针的末端),并且有限自动机方法非常有效(小状态转换表)。因此,我无法设想Sustik-Moore可能是最佳方案……
R .. GitHub停止帮助ICE 2010年

很好的回应-如果我能注视这个特定答案,我会的。
杰森·S

1
@R。sustik-moore算法背后的理论是,当针相对较大而字母相对较小(例如,搜索DNA序列)时,它应该为您提供较大的平均移位量。在这种情况下,更大意味着在给定相同输入的情况下,比基本的Boyer-Moore算法要产生的更大。相对于有限自动机方法或其他一些Boyer-Moore变体(有很多变体),这种方法的效率高低很难说。这就是为什么我强调要花一些时间来研究候选算法的特定优势/劣势的原因。
NealB 2010年

1
嗯,我想我只是在想从Boyer-Moore换来糟糕的角色时才想到转变。不过,随着BM良好的后缀转换的改进,Sustik-Moore可能会优于DFA方法进行DNA搜索。整洁的东西。
R .. GitHub停止帮助ICE,2010年

21

看到讨论中引用了我们的技术报告,我感到很惊讶;我是上面名为Sustik-Moore的算法的作者之一。(我们在论文中未使用该术语。)

我想在此强调,对我而言,该算法最有趣的功能是证明每个字母最多检查一次非常简单。对于较早的Boyer-Moore版本,他们证明每个字母最多检查3次,之后最多检查2次,并且这些证明所涉及的内容更多(请参见论文中的引用)。因此,在介绍/研究此变体时,我也看到了教学上的价值。

在本文中,我们还描述了针对效率的其他变体,同时放宽了理论保证。这是一篇简短的论文,我认为对于一般的高中毕业生来说,这些材料应该是可以理解的。

我们的主要目标是使该版本受到其他可以进一步改进的关注。字符串搜索有很多变体,仅我们一个人不可能想到这个想法可以带来好处的所有方面。(固定文本和更改模式,固定模式的不同文本,是否可以进行预处理,并行执行,在大文本中查找匹配的子集,允许错误,接近匹配等,等等)


1
您是否偶然知道可用的C或C ++实现?我正在考虑将其用于某些dna主题搜索(精确的主题匹配)。如果没有,也许我会尝试自己开发实现并提交提升算法
JDiMatteo 2015年

4
由于没有已知的可用实现,Sustik-Moore / 2BLOCK算法似乎不太可能在实践中使用,并且在摘要论文(如“精确的字符串匹配问题:全面的实验评估”)中的
-JDiMatteo


4

一个非常好的问题。只需添加一些小细节...

  1. 有人在谈论DNA序列匹配。但是对于DNA序列,我们通常要做的是为大海捞针建立一个数据结构(例如后缀数组,后缀树或FM-index),并针对它进行很多匹配。这是一个不同的问题。

  2. 如果有人想对各种算法进行基准测试,那将是非常不错的。关于压缩和后缀数组的构造有很好的基准,但是我没有看到关于字符串匹配的基准。潜在的干草堆候选人可能来自SACA基准

  3. 几天前,我在您推荐的页面上测试了Boyer-Moore实现(编辑:我需要像memmem()这样的函数调用,但它不是标准函数,所以我决定实现它)。我的基准测试程序使用随机的干草堆。该页面中的Boyer-Moore实施似乎比glibc的memmem()和Mac的strnstr()快几倍。如果你有兴趣,实现是在这里和基准测试代码是在这里。这绝对不是一个现实的基准,但这是一个开始。


如果您能与SACA基准测试中的干草堆候选人一起测试,请把它们作为我其他问题的答案,并且如果没有更好的答案,我将其标记为接受。
R .. GitHub停止帮助ICE,2010年

3
关于您的记忆和Boyer-Moore,Boyer-Moore(或者说是Boyer-Moore的增强功能之一)很有可能在随机数据上表现最佳。随机数据具有极低的周期性概率和较长的部分匹配,这会导致二次最坏情况。我正在寻找一种结合Boyer-Moore和Two-Way的方法,或者有效地检测出Boyer-Moore何时“可以安全使用”,但是到目前为止,我还没有取得任何成功。顺便说一句,我不会使用glibc的记忆作为比较。我实现的与glibc基本相同的算法的速度快了好几倍。
R .. GitHub停止帮助ICE,2010年

如我所说,这不是我的实现。感谢Christian Charras和Thierry Lecroq。我可以想象为什么随机输入不利于基准测试,并且我相信glibc选择算法的原因是什么。我也猜想memmem()没有有效实现。我会尝试。谢谢。
user172818 2010年

4

我知道这是一个老问题,但是大多数不良的移位表都是单个字符。如果这对您的数据集有意义(例如,尤其是书面文字),并且有足够的空间,则可以通过使用由n-gram而不是单个字符组成的错误移位表来显着提高速度。




3

更快的“搜索单个匹配字符”(ala strchr)算法。

重要笔记:

  • 这些函数使用“(尾数)(尾数)的数量/计数” gcc编译器内在函数- __builtin_ctz。这些功能可能仅在具有执行该操作的指令的机器(即x86,ppc,arm)上很快。

  • 这些功能假定目标体系结构可以执行32位和64位未对齐负载。如果您的目标体系结构不支持此功能,则需要添加一些启动逻辑以正确对齐读取。

  • 这些功能与处理器无关。如果目标CPU具有矢量指令,则可能会做得更好。例如,strlen下面的函数使用SSE3,可以对其进行微不足道的修改,以对所扫描的字节进行XOR运算以查找除以外的字节0。在运行Mac OS X 10.6(x86_64)的2.66GHz Core 2笔记本电脑上执行的基准测试:

    • 843.433 MB /秒 strchr
    • 每秒2656.742 MB findFirstByte64
    • 每秒13094.479 MB strlen

... 32位版本:

#ifdef __BIG_ENDIAN__
#define findFirstZeroByte32(x) ({ uint32_t _x = (x); _x = ~(((_x & 0x7F7F7F7Fu) + 0x7F7F7F7Fu) | _x | 0x7F7F7F7Fu); (_x == 0u)   ? 0 : (__builtin_clz(_x) >> 3) + 1; })
#else
#define findFirstZeroByte32(x) ({ uint32_t _x = (x); _x = ~(((_x & 0x7F7F7F7Fu) + 0x7F7F7F7Fu) | _x | 0x7F7F7F7Fu);                    (__builtin_ctz(_x) + 1) >> 3; })
#endif

unsigned char *findFirstByte32(unsigned char *ptr, unsigned char byte) {
  uint32_t *ptr32 = (uint32_t *)ptr, firstByte32 = 0u, byteMask32 = (byte) | (byte << 8);
  byteMask32 |= byteMask32 << 16;
  while((firstByte32 = findFirstZeroByte32((*ptr32) ^ byteMask32)) == 0) { ptr32++; }
  return(ptr + ((((unsigned char *)ptr32) - ptr) + firstByte32 - 1));
}

...和64位版本:

#ifdef __BIG_ENDIAN__
#define findFirstZeroByte64(x) ({ uint64_t _x = (x); _x = ~(((_x & 0x7F7F7F7F7f7f7f7full) + 0x7F7F7F7F7f7f7f7full) | _x | 0x7F7F7F7F7f7f7f7full); (_x == 0ull) ? 0 : (__builtin_clzll(_x) >> 3) + 1; })
#else
#define findFirstZeroByte64(x) ({ uint64_t _x = (x); _x = ~(((_x & 0x7F7F7F7F7f7f7f7full) + 0x7F7F7F7F7f7f7f7full) | _x | 0x7F7F7F7F7f7f7f7full);                    (__builtin_ctzll(_x) + 1) >> 3; })
#endif

unsigned char *findFirstByte64(unsigned char *ptr, unsigned char byte) {
  uint64_t *ptr64 = (uint64_t *)ptr, firstByte64 = 0u, byteMask64 = (byte) | (byte << 8);
  byteMask64 |= byteMask64 << 16;
  byteMask64 |= byteMask64 << 32;
  while((firstByte64 = findFirstZeroByte64((*ptr64) ^ byteMask64)) == 0) { ptr64++; }
  return(ptr + ((((unsigned char *)ptr64) - ptr) + firstByte64 - 1));
}

编辑2011/06/04 OP在注释中指出此解决方案具有“无法克服的错误”:

它可以读取经过查找的字节或空终止符,从而可以访问未映射的页面或未经读取许可的页面。除非对齐,否则根本无法在字符串函数中使用大读取。

从技术上讲,这是正确的,但实际上适用于对大于单个字节的块进行操作的任何算法,包括OP在注释中建议的方法

一个典型的strchr实现不是幼稚的,但是比您给出的要有效得多。有关最广泛使用的算法,请参见本文结尾:http : //graphics.stanford.edu/~seander/bithacks.html#ZeroInWord

实际上,它与对齐本身也没有任何关系。没错,这可能会导致使用的大多数常见体系结构上讨论的行为,但这与微体系结构的实现细节有关—如果未对齐的读取跨越4K边界(再次,典型),则该读取将导致程序如果未映射下一个4K页面边界,则终止错误。

但这不是答案中给出的算法中的“错误”-行为是因为函数喜欢strchr并且strlen不接受length参数来限制搜索的大小。char bytes[1] = {0x55};出于我们的讨论目的,搜索恰好位于4K VM页面边界的最末端,而下一个页面未映射,使用strchr(bytes, 0xAA)(其中strchr是一个字节一次的实现)将完全崩溃。同样的方法。strchr相关表兄同上strlen

没有length参数,就无法确定何时应退出高速算法并返回逐字节算法。更可能的“错误”是读取“过去的分配大小”,从技术上来说,这是undefined behavior根据各种C语言标准得出的,并且会通过诸如标记为错误valgrind

总之,任何大于字节块的操作都可以更快地运行,因为这会回答代码和OP指出的代码,但必须具有字节精确的读取语义,如果没有length参数,控制“最后读取”的特殊情况。

该答案中的代码是一个内核,用于在目标CPU具有类似快速ctz指令的情况下快速找到自然CPU字长块中的第一个字节。添加诸如确保其仅在正确对齐的自然边界或某种形式的边界上运行这样的琐事length,这将使您可以退出高速内核,而转入较慢的逐字节检查。

OP在评论中还指出:

至于您的ctz优化,它仅对O(1)尾部操作有所不同。它可以使用细小的字符串来提高性能(例如,strchr("abc", 'a');但肯定不能用任何大号的字符串来提高性能)。

这种说法是否正确,很大程度上取决于所讨论的微体系结构。使用规范的4级RISC管道模型,则几乎可以肯定是正确的。但是,很难说出这对于现代乱序的超标量CPU是否正确,在这种情况下,内核速度会完全使内存流速度相形见.。在这种情况下,“可以退休的指令数”相对于“可以流传输的字节数”之间有很大的差距,这不仅是合理的,而且是很常见的。可以流传输的每个字节可以撤消的指令数”。如果足够大,则ctz可以“免费” 执行+移位指令。


“对于长度为1的针,请使用strchr。”-您要求最快的子串搜索算法。查找长度为1的子字符串只是一种特殊情况,也可以对其进行优化。如果将您当前的特殊情况代码替换为长度为1(strchr)的子字符串,并且执行上述操作,则事情(可能取决于strchr实现方式)会更快。上面的算法比典型的朴素strchr实现快了近3倍。
johne 2011年

2
OP说字符串正确以null终止,因此您的讨论char bytes[1] = {0x55};无关紧要。与您非常相关的评论是,对于任何事先不知道长度的单词读取算法,这都是正确的。
塞斯·罗伯逊

1
该问题不适用于我引用的版本,因为您仅在对齐的指针上使用它-至少这是正确的实现方式。
R .. GitHub停止帮助ICE,

2
@R,它与“对齐的指针”无关。可以想像,如果你有哪些支持与字节级粒度VM保护的架构,每个malloc分配是“充分填充”两边 VM系统强制执行该分配字节粒度的保护....与否的指针对准(假设琐碎的32位int自然对齐是没有意义的-仍然有可能使该对齐的读取读取超过分配大小的值。 超过分配大小的ANYundefined behavior
约翰·约翰逊2011年

5
@johne:+1评论。从概念上讲,您是正确的,但事实是字节粒度保护的存储和执行成本非常高,以至于它们永远也不会存在。如果您知道基础存储是从等效项获得的页面粒度映射mmap,则对齐就足够了。
R .. GitHub停止帮助ICE,

3

只需搜索“最快的strstr”,如果您看到感兴趣的内容,请问我。

在我看来,您对自己施加了太多限制(是的,我们都希望max搜索器具有亚线性线性),但是这需要一个真正的程序员来介入,直到那时我才认为散列方法只是一个精巧的解决方案(对于较短的2..16模式,由BNDM进行了很好的加固)。

只是一个简单的例子:

做着模式(32字节)搜索到字符串(206908949bytes)作为一线...跳过高性能(大-越好):3041%,6801754个跳过/重复Railgun_Quadruplet_7Hasherezade_hits / Railgun_Quadruplet_7Hasherezade_clocks:0/58 Railgun_Quadruplet_7Hasherezade性能:3483KB /时钟

做着模式(32字节)搜索到字符串(206908949bytes)作为一线...跳过高性能(大-越好):1554%,13307181个跳过/重复Boyer_Moore_Flensburg_hits / Boyer_Moore_Flensburg_clocks:0/83 Boyer_Moore_Flensburg性能:2434KB /时钟

将模式(32bytes)搜寻成字串(206908949bytes),以单列方式执行...跳过效能(更好):129%,160239051跳过/重复两次击中次数/两次通过钟:0/816 两次-WAY性能:247KB /时钟


问候Sanmayce


3

(!它的方式是不可思议的)双向算法你提到你的问题最近一直在同一时间提高到多字节字有效工作:最佳盒装字符串匹配

我还没有阅读整篇文章,但似乎他们依赖于一些新的特殊CPU指令(例如,包括在SSE 4.2中),因为它们的时间复杂度要求为O(1),但是如果不可用,它们可以以O(log log w)的时间模拟它们,听起来似乎不太糟糕。


3

您可以实现4种不同的算法。每M分钟(将根据经验确定)对当前实际数据全部运行4次。累计N次运行的统计信息(也待定)。然后在接下来的M分钟内仅使用获胜者。

在Wins上记录统计信息,以便您可以用新算法替换永远不会赢的算法。将优化工作集中在最常规的例程上。更改硬件,数据库或数据源后,请特别注意统计信息。如果可能,在统计日志中包括该信息,因此您不必从日志日期/时间戳中找出该信息。


3

我最近发现了一个不错的工具来衡量各种可用算法的性能:http ://www.dmi.unict.it/~faro/smart/index.php

您可能会发现它很有用。另外,如果必须快速调用子字符串搜索算法,则可以使用Knuth-Morris-Pratt。


感谢您的链接。对于典型情况下的时序而言,这些测试看起来很有趣,但对于捕获最坏情况下的时间却没有吸引力。
R .. GitHub停止帮助ICE

2

您可能还希望针对具有多种类型的字符串的基准进行测试,因为这可能会对性能产生重大影响。算法会根据搜索自然语言(甚至在这里由于形态徽标的不同而可能存在细微的区别),DNA字符串或随机字符串等来执行区分。

字母大小和针头大小也会在许多算法中发挥作用。例如,由于字母大小不同,Horspool的英文文本效果很好,而DNA效果不好,这使得不好的字符规则变得很难。引入后缀可以大大消除这一点。


0

我不知道这是否绝对是最好的,但是我在Boyer-Moore方面有很好的经验。


你知道一种结合博耶-摩尔糟糕的排班表和双向方式的方法吗?Glibc对长针(> 32字节)做了一个变体,但仅检查最后一个字节。问题是双向需要从左到右搜索针的右侧部分,而从右到左搜索时,博耶-摩尔的错误移位最有效。我尝试在“双向”中从左到右使用它(按移位表前进或正常的双向“右半不匹配,以较长者为准”),但在大多数情况下,与普通的双向相比,我的速度降低了5-10%找不到提高性能的任何情况。
R .. GitHub停止帮助ICE,2010年

0

这不会直接回答问题,但是如果文本很大,如何将其分成重叠的部分(重叠图案长度),然后使用线程同时搜索这些部分。关于最快的算法,我认为在Boyer-Moore的变体中,Boyer-Moore-Horspool是最快的算法之一。我在该主题算法中发布了两个Boyer-Moore变体(我不知道它们的名字),其搜索速度比BMH(Boyer-Moore-Horspool)搜索快


0

目前最快的是S. Faro和OM Kulekci的EPSM。参见http://www.dmi.unict.it/~faro/smart/algorithms.php?algorithm=EPSM&code=epsm

针对SIMD SSE4.2(x86_64和aarch64)优化的“精确打包的字符串匹配”。它在所有尺寸上均表现稳定且最佳。

我链接到的站点比较了199种快速字符串搜索算法,而常规算法(BM,KMP,BMH)的运行速度很慢。在这些平台上,EPSM的性能优于所有其他功能。这也是最新的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.