Horspool算法是后缀搜索算法,对于每个文本搜索窗口,将窗口内的最后一个字符与模式串(needle)的最后一个字符进行比较。如果相等,则继续从后向前验证其他字符,直到完全相等或者某个字符不匹配。当遇到字符不匹配的情况时就需要将搜索窗口往后移动,计算移动的距离可以有不同方法,Wikipedia中给出的C语言实现版本是基于窗口的最后一个字符(haystack上的)。
C语言版代码分析:
1 // 在 haystack 中查找 needle 子字符串 2 const unsigned char *horspool_memmem(const unsigned char *haystack, size_t hlen, 3 const unsigned char *needle, size_t nlen) 4 { 5 size_t scan = 0; 6 7 // 单个字符的位移对照表 8 size_t bad_char_skip[UCHAR_MAX + 1]; 9 10 // 参数检查 11 if (nlen <= 0 || !haystack || !needle) 12 return NULL; 13 14 // 初始化位移对照表,缺省值是 needle 的长度,即遇到不匹配时将搜索窗口往后移动 needle 长度 15 // 比如:如果遇到 needle 中没有的字符时,就可以将搜索窗口后移 needle 长度,以跳过该字符 16 for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1) 17 bad_char_skip[scan] = nlen; 18 19 // needle 最后一个字符的下标 20 size_t last = nlen - 1; 21 22 // 遍历 needle 的字符(排除最后一个字符),计算该字符到最后一个字符的位移。 23 // 此处遍历需要从头开始,因为 needle 中可能会出现重复字符,同一个字符必须使用其最后出现位置的位移。 24 // 25 // 排除 needle 最后一个字符的原因是:如果 needle 的最后一个字符在 needle 中是唯一的,那么其位移对照表中的值是 nlen, 26 // 只要当次搜索匹配失败,并且搜索窗口上 haystack 的最后一个字符就是 needle 的最后一个字符,那么就应该将搜索窗口后移 nlen, 27 // 因为该字符没有重复出现 needle 的其它位置上,就可以安全的跳过当前窗口。 28 for (scan = 0; scan < last; scan = scan + 1) 29 bad_char_skip[needle[scan]] = last - scan; 30 31 // 开始搜索匹配 32 // 由于搜索窗口不断往后移动,即 haystack 指针值向后移动,hlen 保存 haystack 的剩余字符串长度。 33 // 当 hlen 的长度小于 nlen 时,查找失败。 34 while (hlen >= nlen) { 35 // 从搜索窗口的尾部向前匹配 36 for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1) { 37 if (scan == 0) // 头部字符匹配则查找成功,返回子字符串位置 38 return haystack; 39 } 40 41 // 如果匹配失败则基于搜索窗口上 haystack 的最后一个字符 haystack[last] 后移搜索窗口 42 hlen -= bad_char_skip[haystack[last]]; 43 haystack += bad_char_skip[haystack[last]]; 44 } 45 46 // 到达此处说明查找失败 47 return NULL; 48 }
查找过程示例:
原始串 haystack: efaboxcbcabcdsdxzcxx needle: abcd 初始化 last: 3 bad_char_skip['a'] = 3 bad_char_skip['b'] = 2 bad_char_skip['c'] = 1 bad_char_skip['d'] = 1 循环1 last: 3 haystack[last]: b bad_char_skip[haystack[last]]: 2 haystack: aboxcbcabcdsdxzcxx needle: abcd 循环2 last: 3 haystack[last]: x bad_char_skip[haystack[last]]: 4 haystack: cbcabcdsdxzcxx needle: abcd 循环3 last: 3 haystack[last]: a bad_char_skip[haystack[last]]: 3 haystack: abcdsdxzcxx needle: abcd 循环4 匹配成功
参考文档