本文程式碼來自於中國大學MOOC
註釋內容為自己理解,如有錯誤請評論,或者私信給我,謝謝
#include <stdio.h>
#include "stdlib.h"
#include "string.h"
typedef int Position;
Position KMP(char string[25], char pattern[7]);
void BuildMatch(char *pattern, int *pInt);
#define NotFound -1
int main() {
char string[] = "this is a simple example";
char pattern[] = "simple";
Position p = KMP(string, pattern);
if (p == NotFound) printf("Not found.\n");
else {
printf("%s\n", string + p);
printf("%d\n", p);
}
return 0;
}
Position KMP(char *string, char *pattern) {
int n = strlen(string);
int m = strlen(pattern);
int s, p, *match;
if (m > n) return NotFound;
match = (int *) malloc(sizeof(int) * m);
// 查詢match最長匹配字串位置值 例如:圖1-1
// pattern a b c a b
// index 0 1 2 3 4
// match -1 -1 -1 0 1
BuildMatch(pattern, match);
s = p = 0;
while (s < n && p < m) {
if (string[s] == pattern[p]) {
s++;
p++;
} else if (p > 0) {
// 將p置為 前p-1個元素 最大子串長度+1
// 如圖1-2
p = match[p - 1] + 1;
} else
s++;
}
return (p == m) ? (s - m) : NotFound;
}
void BuildMatch(char *pattern, int *match) {
int i, j;
int m = strlen(pattern);
match[0] = -1;// -1 表示子串長度不存在,無任何相同的元素
for (int j = 1; j < m; ++j) {
// i表示前j-1個元素最大相同子串長度 陣列索引位置 index-length 0-1
i = match[j - 1];
while ((i >= 0) && (pattern[i + 1] != pattern[j]))
// 第j個下標的字元和(match[j-1]+1)下標上的元素比較
// 如果不匹配,則根據下標為match[j-1]的相同串基礎上進行條件比較
// 因為match[j-1]已經存在,那麼綠紫色整塊和後面綠紫塊肯定一樣
// 又第一個小綠塊為match[match[j-1]],綠塊和紫塊相同
// 所以第一個綠塊和最後一個紫塊相同,只需比較問號位置的值即可
// char[match[match[j-1]]+1] 和 char[j] 的值是否相等
// 如圖 1-3
i = match[i];
if (pattern[i + 1] == pattern[j])
// 如圖 1-4
match[j] = i + 1;
// 如果都匹配不上就直接設定為-1
else match[j] = -1;
}
}
圖1-1
match[j]
的值實際上是前j個(包括j)元素的最大子串長度 對應到陣列中的位置 比如圖中 j = 6;
最大子串(abca)的長度為4,在陣列中的索引為3
圖1-2
當比較到後面不相等時,模式串相當於要後移到從上往下的第三個橫條的情形,也就是把第二個橫條情況p = match[p-1]+1
圖1-3
- 第j個下標的字元和
(match[j-1]+1)
下標上的元素比較 - 如果不匹配,則根據下標為
match[j-1]
的相同串基礎上進行條件比較 - 因為
match[j-1]
已經存在,那麼綠紫色整塊和後面綠紫塊肯定一樣 - 又第一個小綠塊為
match[match[j-1]]
,綠塊和紫塊相同 - 所以第一個綠塊和最後一個紫塊相同,只需比較問號位置的值即可
char[match[match[j-1]]+1]
和char[j]
的值是否相等
圖1-4