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] 为 0while (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;
}