KMP算法的理论部分就不在赘述了,有不懂的小伙伴可以参考这个帖子,感觉写的挺不错的。从头到尾彻底理解KMP
个人比较喜欢简单粗暴的直接上代码,又看不懂的小伙伴可以下下面评论提出来。
下面的程序是为了后面的LZ77 压缩算法做铺垫的,其实现的是从源数据中找到与匹配数据最长的数据的座标和匹配长度。绕的有点晕哈;
举个栗子:
字符串s = “a b a c a a b a c a b a c a b a a b b”;
字符串p = “a b a c a b”;
从字符串s中找到与字符串p匹配的数据的下标和最长匹配数据长度;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
#define TEST_STR 0 //选择测试对象是字符串还是数组
typedef struct KMP{
uchar *src; //源数据
uchar *dst; //要求匹配的数据
ushort sLen; //源数据长度
ushort pLen; //匹配数据长度
ushort coord; //匹配到数据记录其座标
ushort matchLen; //最大的匹配长度
}KMP_TypeDef;
KMP_TypeDef KMP;
bool KMP_Search(KMP_TypeDef *KMP){
int i=0,j=0,max=0,baki=0;
if(KMP->sLen==0 || KMP->pLen==0){
return false;
}
while(i < KMP->sLen){ //把源数据遍历一遍
if(KMP->src[i] == KMP->dst[j]){ //从源数据中找到与匹配数据相同的数据
i++;
j++;
if(j >= max){
max = j; //更新最长匹配数据长度
baki = i; //备份座标值
}
}
else{
i = i - j + 1;
j = 0; //如果失配,则下一次匹配从匹配数据的第一个数据开始
}
}
KMP->coord = baki-max; //找到最佳匹配数据的下标
KMP->matchLen = max; //匹配数据长度
return true;
}
int main(void){
uchar str[100]={0};
KMP_TypeDef kmp;
#if TEST_STR
kmp.src = "a b a c a a b a c a b a c a b a a b b";//"ABCDEFGHIJKLMNOPQRSTUVWXYZd abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdesfghijklmnopqrstuvwxyz";//
kmp.dst = "a b a c a b";//"abcdesfghiu";//
kmp.sLen = strlen(kmp.src);
kmp.pLen = strlen(kmp.dst);
printf("slen:%d - plen:%d\n",kmp.sLen,kmp.pLen);
#else
uchar arr1[] = {0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f};
uchar arr2[] = {0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b};
kmp.sLen = sizeof(arr1)/sizeof(arr1[0]);
kmp.pLen = sizeof(arr2)/sizeof(arr2[0]);
kmp.src = malloc(kmp.sLen * sizeof(arr1[0]));
kmp.dst = malloc(kmp.pLen * sizeof(arr2[0]));
memcpy(kmp.src, arr1, kmp.sLen);
memcpy(kmp.dst, arr2, kmp.pLen);
printf("slen:%d - plen:%d\n",kmp.sLen,kmp.pLen);
#endif
KMP_Search(&kmp);
printf("coord:%d - Len:%d\n",kmp.coord, kmp.matchLen); //打印下标和长度
strncpy(str, kmp.dst, kmp.matchLen); //字符串拷贝,这里拷贝数据也没出错
printf("Match: %s\n",str); //打印字符串,测试数组也将打印成字符串,具体内容可以参照ASCII码表
}
测试结果
-
#define TEST_STR 0
-
#define TEST_STR 1
-
把字符串替换一下
kmp.src ="ABCDEFGHIJKLMNOPQRSTUVWXYZd abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdesfghijklmnopqrstuvwxyz";
kmp.dst = "abcdesfghiu";