KMP算法
来源:互联网 发布:淘宝确认收货 花呗 编辑:程序博客网 时间:2024/06/18 04:43
如果我们有两个字符串,分别是S和P,要找出P在S中的位置,该怎么查找呢?
这种匹配方式成为暴力匹配,BF算法,当 S[i]==P[j] 匹配成功,i++,j++
代码如下:
#include<stdio.h>#include<assert.h>#include<string.h>int BF(const char *str,char *sub){ assert(str!=NULL && sub!=NULL) int slen=strlen(str); int plen=strlen(sub); int i; int j; while(i<slen && j<plen) { if(str[i]==sub[j]) { i++; j++; } else { j=0; i=i-j+1; }}if(j>=plen){ return i-j;}else{ return -1;}}
它的时间复杂度为O(strlen(P)*strlen(S)),这样它的时间复杂度就很大,那么如何缩小它的时间复杂度呢?
就是KMP算法的核心,利用已匹配相同的字符,不让i 回溯,j不停地往后走,让时间复杂度变成O(strlen(p)+strlen(S))。
例如这样两个字符串:
S: BBC ABCDAB ABCDABCDABDE
P: ABCDABD
按照我们之前的暴力匹配来看一下:
当s[i]和p[j]不相等时,i继续往后走,j=0,这是我们之前暴力匹配算法的思路。
A和D 不相等,那么i就要回退到4, j=0,显而易见的是s[i]和p[j]是不匹配的,i又回退到5,j=0,肯定也是不匹配的,那么我们是不是发现了我们绕远了路呢?那么能不能跳过重复的比较匹配过程呢?
这俩个字符串哥们就开始商量,我们怎么样才可以了如执掌呢?
s[i]说 :刚才都是我在找你,跳来跳去还找不到你真是辛苦!
p[j]说:哎呀,我的小心肝,别怕,你别跳了,我来找你,我有一套秘诀,简称KMP算法,马上就能找到你。
KMP算法:利用已经有部分匹配的字符串信息,保证让 i 值不回退,通过修改 j 的值,让p[j] 移动到有效的位置进行匹配。
那么我们再来看看这两个字符串,我们的理想效果是这样的:
额,我们数一下下标的位置:s[9]和p[6]匹配失败时,如果我们让 i 不回退,j的值往后走,那么它应该跳到如上图所示的p[2] 位置,然后 p[2] 再与 s[9]进行匹配,这是不是就完美了。
可我们如何解决让p每次都跳到有效的位置呢?
如果每个数组中的每个值都有一个规定跳到有效位置的K值就好了,那么我们就直接可以用了。
我们假设有这样的一个K值,还是用刚才的例子,来推一推:
这样我们就能找到k的值,所以当p[j]不匹配时,就跳到相对应的k的位置,我们放在数组里next[j]=k。
但我们注意的是:虽然我们找到了K值,也就是不匹配字符p[j]之前重复匹配的字符,但我们一定要找到的是匹配字符的最大长度,而这个最大长度才是next数组存在的意义。如何找到最大长度呢?
1、子串在失配前找两个相等的最长真子串(不包含自己),一个以0下标作为开头,另一个以失配前的字符为结尾。
例如:
ABCDABD 求它每个字符存在的最大相同字符串
A B C D A B D
next[j] -1 0 0 0 0 1 2
再看一个例子: ABCDABCDABDE
A B C D A B C D A B D E
next[j] -1 0 0 0 0 1 2 3 4 5 6 0
例3: a b a b a b a b a a a b b b a b c d a b
next[j] -1 0 0 1 2 3 4 5 6 7 1 1 2 0 0 1 2 0 0 1
我们知道了next[j]了,可是我们能不能求出next[j+1]的值呢?这样就知道next[j]数组的所有值,就能直接用这个数组来找到合适的移动位置。
我们一起找一下规律吧,用一下上面的一个例子:
0 1 2 3 4 5 6 7 8 9
a b a b a b a b a a a b b b a b c d a b
next[j] -1 0 0 1 2 3 4 5 6 7 1 1 2 0 0 1 2 0 0 1
观察next数组,我们知道next[0]=-1,next[1]=0,是不变的。
也就是说next[j]=k,如何求next[j+1]?
next[j]=k,是通过表达式 p0………pk-1=pj-k……..pj-1 求得的 ,
if pk=pj
两式相加 :p0 ......pk=pj-k.....pj-1 pj k+1=k+1 next[j+1]=k+1 验证一下:next[4]=2 那么next[5]=? k=2 j=4 p[2]==p[4]==a 那么next[5]=3,查上面已经得出的下标5的next值为3 if pk!=pj 呢? 例如我们要知道next[9]=7,求 next[10]? j=9 k=7 1. p[j] !=p[k] 那只能接着往下找,直到pk==pj next[7]=5 k=5 2.p[j]!=p[k] next[5]=3 k=3 3. p[j]!=p[k] next[3]=1 k=1 4.p[j]!=p[k] next[1]=0 k=0 5.p[j]==p[k] next[10]=0+1=1
哎呀,5次查找之后,终于找到了 。
代码实现:
#include<stdio.h> #include<assert.h> #include<stdlib.h> #include<string.h>static void Getmaxnext(const char *sub,int *next){ int len=strlen(sub); next[0]=-1; next[1]=0; int j=1; int k=0; while(j+1<len) { if(k==-1 || sub[j]==sub[k]) { next[++j]=++k; } else { k=next[k]; } }}int KMP(const char *str,const char *sub,int pos){ if(pos<0) { return -1; } int strlen=strlen(str); int sublen=strlen(sub); int i=pos; int j=0 ; int *next=(int *)malloc(sizeof(int)*sublen); Getmaxnext(sub,next); while(i<strlen && j<sublen) { if(j==-1 && str[i]==sub[j]) { i++; j++; } else { j=next[j]; } } free(next); if(j>=sublen) { return i-j; } else { return -1; }}
KMP优化:
刚才我们算next[9]=7,求 next[10]?
j=9 k=7
1. p[j] !=p[k] 那只能接着往下找,直到pk==pj
next[7]=5 k=5
2.p[j]!=p[k]
next[5]=3 k=3
3. p[j]!=p[k]
next[3]=1 k=1
4.p[j]!=p[k]
next[1]=0 k=0
5.p[j]==p[k] next[10]=0+1=1
经过了5次查找,前4次由于p[j]!=p[k],所以一直在查找,p[9]=a, p[7]=a;而接下来的p[k]==p[7],那怎么又能找到呢?所以就多走了几步,如何省掉这些多走的路呢?
0 1 2 3 4 5 6 7 8 9
a b a b a b a b a a a b b b a b c d a b
next[j] -1 0 0 1 2 3 4 5 6 7 1 1 2 0 0 1 2 0 0 1
还是通过next[9] 求next[10]?
nextval:-1 0 -10 -10 -1 0 -1-1-1 000 -1 0 2 0-10
让每次跳转到的字符如果相同的话,就设成一样的值。
p[9]的nextval=-1,k=0, p[0]==p[9]==a;
next[10]=0+1=1
是不是快很多了呢?
代码实现,改动next 数组求法:
#include<stdio.h> #include<assert.h> #include<stdlib.h> #include<string.h>static void Getmaxnext(const char *sub,int *next){ int len=strlen(sub); next[0]=-1; next[1]=0; int j=1; int k=0; while(j+1<len) { if(k==-1 || sub[j]==sub[k]) { if(sub[j]!=sub[k]) { next[++j]=++k; } else { next[j]=next[k]; } } else { k=next[k]; } }}
还有两个简单的例子再理解一下:
- KMP算法详解 【KMP】
- 【KMP】KMP算法模板
- KMP hihoCoder1015 KMP算法
- kmp算法
- KMP算法
- KMP算法
- KMP算法
- KMP算法
- KMP 算法
- kmp算法
- KMP算法
- kmp算法
- KMP算法
- KMP算法
- kmp算法
- kmp算法
- KMP算法
- KMP算法
- HDU 1848 Fibonacci again and again 博弈论-SG函数
- 学习linux的简单介绍
- 用shape画内圆外方,形成一个圆形头像
- [FZUOJ
- Spring黑马笔记入门八
- KMP算法
- 有关二维数组与指针的应用:3个学生各学4门课,计算总平均分,并输出第n个学生成绩。小白撸代码....
- 【SSM】Eclipse使用Maven创建Web项目+整合SSM框架
- 【HDU
- fabric协议规范
- 聪明
- C++String类
- HYSBZ2141-排队
- 【Android】之 活动(Activity)