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)。
例如模式串Patten = "abaabcac"。其next數組如圖所示:
我們可以看到這次的匹配在藍色的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; };