Rabin-Karp:把窗口压成一个数
Naive matching、KMP、Boyer-Moore 都在比较字符。Rabin-Karp 换了一种思路:先把 pattern 算成一个哈希值(一个数),再把 text 里每个等长窗口也算成哈希值,两个数相等才值得去逐字符确认。关键技巧是 rolling hash——窗口右滑一格时,不必重新扫一遍,只要 O(1) 地「减掉滑出去的最高位、整体乘进位、加上新进来的低位」就能更新。
多项式哈希: 把长度为 m 的窗口看成 base 进制的一个数 <code>h = c₀·base^(m-1) + c₁·base^(m-2) + … + c<sub>m−1</sub></code>(对一个大质数取模)。本 demo 取 base=256、mod=1e9+7,字符值用其码点。滑动更新:<code>h ← (h −
c<sub>out</sub>·base^(m-1))·base + c<sub>in</sub></code>(再取模)。
1 · 哈希相等 ≠ 字符串相等:spurious hit
哈希把任意长度的窗口压成一个固定范围里的数,必然有不同字符串撞到同一个值——这叫 collision。所以哈希命中只是「疑似」,必须逐字符确认;确认失败的那次就是 spurious hit,在上面单步时会被标红。本 demo 用了很大的模数,短输入下真实 collision 极其罕见,但确认这一步在算法里永远不能省——它是 Rabin-Karp 保证正确性的必要步骤。
优势: 单模式查找中它未必最快,但天生适合同时查找多个 pattern(把它们的哈希放入一个集合,每个窗口只算一次哈希即可查全部),也是 rsync / 大文件去重 / 剽窃检测 中「滑动指纹」的基础思想。
Naive matching、KMP、Boyer-Moore、Rabin-Karp 都是「在线扫描」算法:边扫描边比较。另一类思路是先把 patterns 或 text 预处理成结构再反复查询,见 Trie、Aho-Corasick 与 suffix array 等页。