好吧,我不像个白痴,我要更明确地陈述问题/需求:
- Needle(模式)和haystack(要搜索的文本)都是c风格的以空值结尾的字符串。没有提供长度信息;如果需要,则必须进行计算。
- 函数应该返回指向第一个匹配项的指针,如果没有找到匹配项,则返回
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通过向后扫描(从右到左)充分利用了它,而Two-Way则需要从左到右扫描。
- 对于一般情况(没有内存不足或二次性能条件),我找到的唯一两个可行的候选算法是双向和有序字母的字符串匹配。但是,是否存在容易发现的情况,不同的算法是最优的?当然,空间算法中的许多
O(m)
(其中m
是针的长度)可以用于m<100
左右。也有可能使用最坏情况下二次的算法,如果有一个简单的测试针,证明只需要线性时间。
加分点:
- 您是否可以通过假设针和草垛都是格式良好的UTF-8来提高性能?(对于不同字节长度的字符,格式良好会在针和干草堆之间增加一些字符串对齐要求,并允许在遇到不匹配的头字节时自动进行2-4个字节的移位。但是除了最大后缀计算、良好的后缀移位等等,这些约束条件能给你带来什么吗?)
注意:我很清楚大多数算法,只是不知道它们在实践中的表现如何。这里有一个很好的参考,这样人们就不会一直把算法作为评论/答案给我参考:http://www-igm.univ-mlv.fr/~lecroq/string/index.html