KMP算法C语言实现详解
作者:老九—技术大黍
社交:优快云
公众号:老九学堂(新手有福利)
特别声明:原创不易,未经授权不得转载或抄袭,如需转载可联系笔者授权
前言
在讲解算法时我先参考了O'Reilly出版的《Mastering Algorithms with C》、Wiki网站和严蔚敏的《数据结构(C语言版)》教材。因为,我们希望通过多方面专业的参考,希望学习算法时是非常严谨的。
KMP简介
我没有在《Mastering Algorithms with C》一书找到KMP的定义,我觉得维基上的解释是标准的,所以截图如下:
关于这个算法的解释网上有很多,我在这里就不参考翻译,也不再累述了。我们列出这个维基解释处,还列出一个针对KMP算法的网站应用解释:brilliant.org/wiki/knuth-… 大家可以自行参考理解,希望可以帮助大家多方面的认知该KMP的准确定义。
下面我们的介绍C语言实现过程。
KMP算法C语言实现
思路:
字符串匹配是计算机的基本任务之一。举个“栗子”:有一个字符串"aaaaaacaaacac",我想知道,里面是否包含另一个字符串"aaaac"?这里就会使用到串的模式匹配算法。串的模式匹配算法有很多种,最常见的是传统的BF算法和KMP算法。
BF算法的设计思想
- 主串和模式串逐个字符进行比较
2. 当出现字符不相同(失配)时,主串的比较位置重置为起始位置的下一个字符位置,模式串的比较位置重置为起始位置
3. 匹配成功返回主串中匹配串的起始位置,否则返回错误码
BF算法的设计缺陷
在BF算法中,每次失配都需要回溯指向上次比较起始字符的下一个字符!我们经过观察会发现:在回溯的时候,已匹配部分似乎有一部分没必要再比较了!这样就可以降低算法的时间复杂度。
BF算法的设计缺陷的解决方案
KMP(Knuth-Morris-Pratt)算法有效的解决了BF算法的缺陷。它以三个发明者命名,第一个字符K就是著名科学家Donald·Knuth。
但是这种算法不太容易理解,网上有很多解释,但读起来都很费劲。以下将给大家详细的介绍KMP算法的设计思想和工作原理。
KMP算法的设计思想
在匹配过程中出现字符比较不相等时,主串S已比较的位置不回溯,模式串T比较的位置进行移动。
在匹配过程中有一个难题需要解决:如何计算模式串T在失配时的移动位数?经过三位牛人的观察思考,总结设计出部分匹配函数。
部分匹配函数是KMP算法中最难以理解的部分。首先需要理解前缀、后缀和最大共有长度的概念。
前缀指除了最后一个字符以外,一个字符串的全部头部组合。
后缀指除了第一个字符以外,一个字符串的全部尾部组合。
最大共有长度(部分匹配值)指前缀和后缀中最大共有元素,没有则为0。例如“abab”的前缀为“a”、“ab”、“aba”,后缀为“b”、“ab”、“bab”,所以最大共有元素为“ab”,最大共有长度为2。
回顾一下KMP算法的匹配过程:
红线框出的部分恰好就是失配时已匹配部分“aaaa”的最大共有元素“aaa”,这一部分字符也就是不需要再重复比较直接跳过的字符。
在代码实现过程中,j移动后的位置=模式串T的起始下标+部分匹配值。通常起始下标为0,因此j移动后的位置=部分匹配值,即j=next[j],next[j]就是部分匹配函数,j为失配时的位置。
因此接下来就成了对部分匹配函数的实现。将“aaaac”以首字符起始的所有子串的最大共有长度枚举出来,构成部分匹配表,它描述了失配时的下标j与部分匹配值的关系。
部分匹配表则是通过模式串T的自匹配实现:
C语言代码实现
KMP匹配算法
int KMPCompare(HString parent, HString child, int pos){
int next[255];
Get_Next(child, next);
int i = pos - 1;
int j = 0; //j用于子串child中的起始位置
while(i < parent.length && j < child.length){
if(j == 0 || parent.ch[i] == child.ch[j]){
++i;
++j;
}else{
j = next[j]; //i不变,j后退
}
}
if(j == child.length){
return (i + 1) - j;
}
return 0;
}
部分匹配函数的实现
void Get_Next(HString child, int * next){
int i = 0;
int j = -1;
next[0] = -1; //不会用到
while(i < child.length){
if(j == -1 || child.ch[i] == child.ch[j]){
++i;
++j;
next[i] = j;
}else{
j = next[j];
}
}
}
main函数
void main(){
/*使用KMP算法匹配串*/
HString parent, child;
StrAssign_HeapString(&parent, "BBC ABCDAB ABCDABCDABDE"); //字符串赋值函数略
StrAssign_HeapString(&child, "ABCDABD");
printf("Index = %d\n", KMPCompare(parent, child, 1));
}
最后
我们给出的代码实现肯定不是最好,这里只是抛砖引玉,希望给大家不一样的C语言的代码实现,如果有不足之处,希望大家指正和补充。
记得给大黍❤️关注+点赞+收藏+评论+转发❤️
作者:老九学堂—技术大黍
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。