字符串查找算法中,最著名的两个是KMP算法(Knuth-Morris-Pratt)和BM算法(Boyer-Moore)。两个算法在最坏状况下均具备线性的查找时间。可是在实用上,KMP算法并不比最简单的c库函数strstr()快多少,而BM算法则每每比KMP算法快上3-5倍。可是BM算法还不是最快的算法,这里介绍一种比BM算法更快一些的查找算法。
例如咱们要在"substring searching algorithm"查找"search",刚开始时,把子串与文本左边对齐,html
substring searching algorithm |
结果在第二个字符处发现不匹配,因而要把子串日后移动。可是该移动多少呢?这就是各类算法各显神通的地方了,最简单的作法是移动一个字符位置;KMP是利用已经匹配部分的信息来移动;BM算法是作反向比较,并根据已经匹配的部分来肯定移动量。这里要介绍的方法是看紧跟在当前子串以后的那个字符(上图中的'i')。函数
显然,无论移动多少,这个字符是确定要参加下一步的比较的,也就是说,若是下一步匹配到了,这个字符必须在子串内。因此,能够移动子串,使子串中的最右边的这个字符与它对齐。如今子串'search'中并不存在'i',则说明能够直接跳过一大片,从'i'以后的那个字符(即‘n’)开始做下一步的比较,以下图:spa
substring searching algorithm |
比较的结果,第一个字符就不匹配,再看子串后面的那个字符,是'r',它在子串中出如今倒数第三位,因而把子串向前移动三位,使两个'r'对齐,以下:code
substring searching algorithm
search
哈!此次匹配成功了!回顾整个过程,咱们只移动了两次子串就找到了匹配位置,是否是很神啊?!能够证实,用这个算法,每一步的移动量都比BM算法要大,因此确定比BM算法更快。htm
下面是这个算法的c代码。注意我假设了每一个字符的值都介于0-127之间(即纯ascii码)。blog
1 #ifndef SUNDAY_H 2 #define SUNDAY_H 3 4 /******************************************************** 5 Sunday算法: 6 假设源字符串为src,匹配串为pattern。 7 若是src的某字符不匹配: 8 (1)该字符在pattern中,且在pattern中最右边的位置为n。那么 9 下次匹配直接移动pattern使得n的位置和该字符的位置对应的地方。 10 (2)该字符不在pattern中,显然没有比较的意义,则直接跳过去, 11 将pattern的头部移到与该字符下一个字符对应的位置。 12 *********************************************************/ 13 14 #include <string.h> 15 #include <stdio.h> 16 17 // 假设出现的字符都是0~127范围内的,即都是ascii字符 18 char *sunday(const char *src, char *pattern) { 19 if (NULL == src) 20 return NULL; 21 if (NULL == pattern) 22 return (char *)src; 23 24 int len1, len2, shift[128]; 25 26 len1 = strlen(src); 27 len2 = strlen(pattern); 28 29 // construct shitf table 30 for (int i = 0; i < 128; i++) 31 shift[i] = len2 + 1; // 默认是移动len2+1,即直接跳过 32 33 // adjust shift table 34 const char *p; 35 for (p = pattern; *p; p++) 36 shift[*p] = len2 - (p - pattern); // p-pattern为字符相对于第一个元素的偏移量,len2-(p-pattern)则表示从右往左数的偏移量,即要移动的步数 37 38 const char *s, *pSrc = src; 39 // start search 40 while (pSrc + len2 <= src + len1) { // 若是pSrc + len2不越界(若越界,则说明src剩余未匹配的字符数量小于pattern的长度,则确定不会匹配成功) 41 for (p = pattern, s = pSrc; *p; ++p, ++s) 42 if (*p != *s) // mistach 43 break;// break内循环 44 45 if ('\0' == *p) // found it! 46 return (char *)pSrc; 47 48 pSrc += shift[pSrc[len2]]; // pSrc[len2]取出下一个须要匹配的字符,shift[pSrc[len2]]取出该字符须要让指针移动多少步 49 } 50 51 return NULL; 52 } 53 54 #endif
下面是main:ci
1 #include "Sunday.h" 2 3 int main() { 4 char *src = "123456789", *patt = "234"; 5 char *ret = sunday(src, patt); 6 7 printf("%s\n", ret); 8 9 return 0; 10 }
代码最后:pSrc += shift[pSrc[len2]];这里。字符串
举个例子来理解下,好比:
123456789
237
那么4和7不匹配,len2就表示'5'这个位置,因此pSrc[len2]表示取出'5'这个字符。
因此pSrc下次就移动到'5'这个位置从新开始匹配了。