字符串查找算法中,最著名的兩個是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'這個位置從新開始匹配了。