KMP演算法(next演算法)
KMP演算法之前需要說一點串的問題:
串:
字串:ASCII碼為基本資料形成的一堆線性結構。
串是一個線性結構;它的儲存形式:
typedef struct STRING {
CHARACTER *head;
int length;
};
樸素的串匹配演算法:
設文字串text = "ababcabcacbab",模式串為patten = "abcac" 其匹配過程如下圖所示。
黑色線條代表匹配位置,紅色斜槓代表失配位置
演算法說明:
一般匹配字串時,我們從目標字串text(假設長度為n)的第一個下標選取和patten長度(長度為m)一樣的子字串進行比較,如果一樣,就返回開始處的下標值,不一樣,選取text下一個下標,同樣選取長度為n的字串進行比較,直到str的末尾(實際比較時,下標移動到n-m)。在普通的匹配中,假如從文字串的第i個字元來開始於模式串匹配。當匹配到模式串的第j位發現失配,即text[i+j] != patten[j]的時候,我們又從文字串的第i+1個位置來重新開始匹配。儘管我們已經知道了好多字元其實根本就匹配不上,我們還是進行了這個過程,這個時候回溯的過程會非常耗費我們的時間。這樣的時間複雜度是O(n*m)
程式碼如下:
int search(const char*str,const char *subStr) { int strlen = strlen(str); int subStrlen = strlen(subStr); int i; int j; for(i = 0;i <= strlen - subStrlen;i++){ for(j = 0;j < subStrlen;j++){ if(str[i + j] != subStr[j]) break; }判斷subStrlen是否比較完成 } }
KMP演算法:
而KMP演算法的實質就是,當遇到text[i+j] != patten[j]的時候,但是我們知道模式串中的 0~j-1 位置上的字元已經於i ~ i+j-1位置上的字元是完全匹配的。就不再重新從text[i+1]開始匹配,而是根據next陣列的下標找到patten的下標,從那個下標開始匹配。從而時間複雜度為O(m+n)。
我們可以看到這次的匹配在藍色的c失配了,而c的下標為5,他的next陣列的下標為2。因此,下次的匹配不再是從text[1]開始,而是從text[2]開始,這樣就省去了不必要的比較。
程式碼如下:
#include <stdio.h> #include <malloc.h> #include <string.h> #include "kmpmec.h" void getNext(const char *str, int *next); int KMPSearch(const char *str, const char *subStr); int KMPSearch(const char *str, const char *subStr) { int strLen = strlen(str); int subLen = strlen(subStr); int *next; int i = 0; int j = 0; if (strLen <= 0 || subLen <= 0 || strLen < subLen) { return -1; } next = (int *) calloc(sizeof(int), subLen); if (subLen > 2) { getNext(subStr, next); } while (strLen - i + next[j] >= subLen) { while (subStr[j] != 0 && str[i] == subStr[j]) { i++; j++; } if (subStr[j] == 0) { free(next); return i - subLen; } else if (j == 0) { i++; j = 0; } else { j = next[j]; } } free(next); return -1; } void getNext(const char *str, int *next) { //得到next陣列 int i = 2; int j = 0; boolean flag; next[0] = next[1] = 0; //next陣列的前兩個下標一定為零 for (i = 2; str[i]; i++) { for (flag = TRUE; flag;) { if (str[i-1] == str[j]) { //通過比較失配位置的前一個和前一個下標元素的比較,獲取next陣列的下標。 next[i] = ++j; flag = FALSE; } else if (j == 0) { next[i] = 0; flag = FALSE; } else { j = next[j]; } } } } int main(void) { char str[80]; char subStr[80]; int result; printf("請輸入源字串:"); gets(str); printf("請輸入子字串:"); gets(subStr); result = KMPSearch(str, subStr); if (result == -1) { printf("字串[%s]不存在子串[%s]\n", str, subStr); } else { printf("子串[%s]第一次出現在字串[%s]中的下標為%d\n", subStr, str, result); } return 0; };