KMP字串匹配演算法
next陣列的計算方法:
看該字元前的字串的字首和字尾有多少相同(可以交叉重疊),就讓相同的數量值加一即為當前next值。
也可以這樣計算:看前一個字元的next值處是否與前一個字元相同,若相同,則當前next值為上一next值加一;若不相同,則檢視上一next值處的字元的next處於當前的字元是否相同,直到相同之後,讓相同的值加一(如果到達最開頭都不相同,則直接設定為1,總之就是到相同的位置的next值加一)。
下面是一個例子:
A | B | C | A | B | C | D |
---|---|---|---|---|---|---|
0 | 1 | 1 | 1 | 2 | 3 | 4 |
因此,在第一次失配時,如上圖紅框區域,j
指標直接回到了next[j]
的位置,當j
位於第一個時(即第一個就失配),i++並且j不變,以此類推
nextval陣列的計算方法
規則:
對比當前next值處的字元是否與當前字元相同,若相同,則nextval也與那字元的nextval相同;若不同,則nextval與其next值相同;
例子:
索引 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
字串 | a | b | a | b | a | a | a | b | a | b | a | a |
next陣列 | 0 | 1 | 1 | 2 | 3 | 4 | 2 | 2 | 3 | 4 | 5 | 6 |
nextval陣列 | 0 | 1 | 0 | 1 | 0 | 4 | 2 | 1 | 0 | 1 | 0 | 4 |
解釋: | 前一個是b,b和a不相同,到第一個,設定為1 | 前一個是a,與b不一樣,和b還是不一樣,和a一樣,1+1=2 |
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
using namespace std;
#define MAXSTRLEN 255
typedef unsigned char SString[MAXSTRLEN + 1];
void strLength(SString S) {
int m;
for (m = 1; S[m] != '\0'; m++);
S[0] = m - 1;
}
void get_next(SString T, int* next) {
int j = 1, k = 0;
next[1] = 0; // 初始化 next[1] 為 0
while (j < T[0]) { // T[0] 是串 T 的長度
if (k == 0 || T[j] == T[k]) {
j++;
k++;
next[j] = k;
}
else {
k = next[k];
}
}
}
int Index_KMP(SString S, SString T, int pos, int* next) {
int i = pos, j = 1; // i 指向主串 S 的當前字元,j 指向模式串 T 的當前字元
get_next(T, next); // 計算模式串 T 的 next 陣列
while (i <= S[0] && j <= T[0]) {
if (j == 0 || S[i] == T[j]) { // 如果匹配,或 j = 0(即從頭匹配)
i++;
j++;
}
else {
j = next[j]; // 如果失配,j 跳轉到 next[j]
}
}
if (j > T[0]) return i - T[0]; // 匹配成功
else return 0; // 匹配不成功
}
int main() {
SString S, T;
int pos;
int next[MAXSTRLEN];
int r;
printf("輸入主串 S: ");
scanf("%s", S + 1); // 跳過下標為 0 的元素
printf("輸入模式串 T: ");
scanf("%s", T + 1); // 跳過下標為 0 的元素
printf("輸入起始位置 pos: ");
scanf("%d", &pos);
strLength(S); // 求主串 S 的長度
strLength(T); // 求模式 T 的長度
if (r = Index_KMP(S, T, pos, next))
printf("模式串在主串中的位置為: %d \n", r);
else printf("匹配失敗!");
return 0;
}