一、算法背景介绍(我们为什么要采用这种算法?)
1.补充定义:
(1)主串:待匹配的大字符串
(2)模式串:我们希望在主串中匹配到的字符串
2.从暴力匹配到KMP算法
(1)暴力匹配算法
谈到KMP算法,离不开比较的是我们的暴力匹配算法,即从主串和模式串第一个字符开始比较,当出现失配时,我们将模式串中的比较字符回到第一位,主串回溯到开始比较的字符的下一位。代码如下。
点击查看代码
int IndexString(char* Original_string, char* Pattern_string) {
int length_OriStr = strlen(Original_string);
int length_PatStr = strlen(Pattern_string);
for (int i = 0; i < length_OriStr; ) {//for循环相比while更加直观,两个终止条件均为不到字符串终点
for (int j = 0; j < length_PatStr; ) {
if (*(Original_string + i) == *(Pattern_string + j)) {
i++;
j++;
}
else {//失配
j = 0;
i = i - j + 1;
}
if (j == length_PatStr) {
return i - j;//返回匹配的字符串在主串中第一个相同字符的下标。
}
}
}
return False;//未找到
}
二、算法的具体实现(附代码)
1.关于串的定义
点击查看代码
#define True 1
#define False 0
typedef int status;
typedef struct {
char* string;
int length;
}HString;
点击查看代码
status Getnext(int* next,HString Pattern_string) {//得到nextj数组
if (next == NULL) {
return False;
}
next[0] = -1;
int k = next[0];//k用来存放对应next[j]
int j = 0;
while (j < Pattern_string.length-1) {
//因为最后会有j++,所以要有length-1,否则超限;或者理解为每次都在求解当前位置对应下一位置的next[j]值,所以要-1
if (k == -1 || *(Pattern_string.string + k) == *(Pattern_string.string + j)) {//没有匹配字符或当前两个字符相等
j++;
k++;
next[j] = k;//后一个的nextj=前一个nextj+1
}
else {
k = next[k];//回溯
}
}
return True;
}
点击查看代码
int Index(HString Original_string, HString Pattern_string) {
int judge = -1;//标识符,默认模式串不存在
int i = 0, j = 0;//i用来遍历主串,j用来跳转模式串
int numx = 0;//用来保存每个第一次比较的字符在主串中的位置
int* next = (int*)malloc(sizeof(int) * Pattern_string.length);//nextj数组
if (next == NULL) {
exit(1);
}
Getnext(next, Pattern_string);//调用上面的函数求得模式串next[j]数组
for (i = 0; i < Original_string.length; ) {
if (j==-1 || *(Original_string.string + i) == *(Pattern_string.string + j)) {
i++;
j++;
if (j == Pattern_string.length) {//完全相同后跳出
break;
}
}
else {
j = *(next+j);//跳转到next[j]对应位置
}
}
if (j == Pattern_string.length) {
judge = i - j + 1;//返回完全匹配后,模式串中第一个字母在主串第一次出现的次序(不是下标)
}
free(next);
return judge;
}
三、算法的理论推导
写了四页纸,比较草,抽空整理一下传上来。
四、算法总结
1.算法步骤:
(1)先进行模式串“自己和自己的比较”,以此求得next[j]数组,从而在后续的主串与模式串匹配中直接跳跃,避免重复性的“自己和自己比较”。
(2)然后利用next[j]数组进行比较
2.评价:经典算法,算是学到的第一个理解透彻比较困难的算法,自己进行推导有助于理解,理解后算法不需要死记硬背即可写出。