【发布时间】:2011-03-12 03:31:16
【问题描述】:
好的,所以我听起来不像个白痴,我将更明确地说明问题/要求:
- Needle(模式)和 haystack(要搜索的文本)都是 C 样式的以空值结尾的字符串。没有提供长度信息;如果需要,必须进行计算。
- 函数应返回指向第一个匹配项的指针,如果未找到匹配项,则返回
NULL。 - 不允许出现故障情况。这意味着任何具有非常量(或大常量)存储要求的算法都需要有一个用于分配失败的回退情况(并且回退处理中的性能会导致最坏情况下的性能)。
- 使用 C 语言实现,尽管在没有代码的情况下对算法(或指向此类的链接)进行良好描述也可以。
...以及我所说的“最快”:
- 确定性
O(n)其中n= 干草堆长度。 (但如果将通常为O(nm)的算法(例如滚动哈希)与更强大的算法相结合以提供确定性的O(n)结果,则可以使用它们的想法。 - 从不执行(可衡量;
if (!needle[1])等的几个时钟都可以)比天真的蛮力算法更差,尤其是在可能最常见的情况下非常短的针头上。 (无条件的大量预处理开销是不好的,因为试图以牺牲可能的针头为代价来提高病理针头的线性系数也是如此。) - 与任何其他广泛实施的算法相比,给定任意针和草垛,性能相当或更好(搜索时间不差 50%)。
- 除了这些条件之外,我将对“最快”的定义保持开放式。一个好的答案应该能解释为什么您认为您建议的方法“最快”。
我当前的实现比 glibc 的 Two-Way 实现慢大约 10% 到快 8 倍(取决于输入)。
更新:我目前的最优算法如下:
- 对于长度为 1 的针,请使用
strchr。 - 对于长度为 2-4 的指针,使用机器字一次比较 2-4 个字节,如下所示:在 16 位或 32 位整数中预加载指针并进行移位,然后循环从大海捞针中取出旧字节/循环输入新字节在每次迭代中。 haystack 中的每个字节都只读取一次,并会针对 0(字符串结尾)和 16 位或 32 位比较进行检查。
- 对于长度 >4 的针,使用带有错误移位表(如 Boyer-Moore)的双向算法,该移位表仅适用于窗口的最后一个字节。为了避免初始化 1kb 表的开销,这对于许多中等长度的针来说将是一个净损失,我保留了一个位数组(32 字节)来标记移位表中的哪些条目被初始化。未设置的位对应于从未出现在针中的字节值,因此可以进行全针长度移位。
留在我脑海中的大问题是:
- 有没有办法更好地利用坏班次表? Boyer-Moore 通过向后(从右到左)扫描来充分利用它,但双向需要从左到右扫描。
- 对于一般情况(没有内存不足或二次性能条件),我发现的唯一两个可行的候选算法是Two-Way 和String Matching on Ordered Alphabets。但是是否存在易于检测的不同算法最佳的情况?当然,空间算法中的许多
O(m)(其中m是针长度)可以用于m<100左右。如果有一个简单的针测试可证明只需要线性时间,那么也可以使用最坏情况二次算法。
奖励积分:
- 您能否通过假设 needle 和 haystack 都是格式良好的 UTF-8 来提高性能? (对于不同字节长度的字符,格式良好会在针头和干草堆之间施加一些字符串对齐要求,并在遇到不匹配的头字节时允许自动 2-4 字节移位。但是这些约束是否会给您带来很多/超出什么最大后缀计算、良好的后缀移位等已经为您提供了各种算法?)
注意:我很了解大多数算法,只是不知道它们在实践中的表现如何。这是一个很好的参考,所以人们不会一直给我关于算法的参考作为 cmets/answers:http://www-igm.univ-mlv.fr/~lecroq/string/index.html
【问题讨论】:
-
Algorithms on Strings 列出了相当多的字符串搜索算法。您可能想要描述您考虑过此列表中的哪些算法。
-
最后那个链接是金!
-
我不敢相信你还没有接受答案。
-
@Mehrdad:我正要说没有任何答案可以真正解决所问的问题,但您的似乎是。在你回答的时候,我已经继续前进,并留下了对
strstr的进一步改进作为以后的事情,所以我实际上还没有开始正确阅读你链接的论文,但听起来确实很有希望。感谢并抱歉没有回复您。
标签: c algorithm string substring