kmp算法
来源:互联网 发布:mac如何彻底关闭程序 编辑:程序博客网 时间:2024/04/29 01:58
1. 朴素匹配算法
过程如图:
翻译成代码:
int index_bf(char *s, char *p){ if(s==NULL || p==NULL) return -1; int i=0,j=0; while(s[i+j]!='\0' && p[j]!='\0') { if(s[i+j]==p[j]) j++; else { j=0; i++; } } if(p[j]=='\0') return i; else return -1;}
2. kmp匹配过程
如图:
1. 可以看到匹配过程中 i 跟 j 是同时增加的,不同于朴素匹配算法只增加 j 。 在 i=5, j=5的时候有c!=d,这个时候的处理过程如下图
2. 这里模式串被右移了3个位置,实际上就是j=5变成j=2(next[5]=2 为什么??)。i=5保持不变,继续匹配直到i: 5->8, j:2->5。上图绿色部分ab两个字符我们没有比较过,因为next[5]=2隐含了前面2个字符不需要在比较的意思。
3. next[j]=k的含义
1. 直观的含义是::设置j=k, 即把模式串右移j-k个位置。 如:next[5]=2,表示j=2即模式串右移5-2=3个位置。
2. 更进一步的含义是:模式串前面k的字符已经比较过,不需要再比较了,直接从j=k(因为下标从0开始)这个位置开始比较
3. 再进一步的含义可以用下面的图表示(next[5]=2 ):我们发现了在k=2这个位置,把模式P[0-4]分成了3个部分 "P[0]P[1]"="P[3]P[4]"还有个就是P[2]这3个部分。
举个列子上面的next[5]=2是因为:
4. 用一个比较专业的术语来描述就是,在P[0~j-1]中找一个最长真前缀使得它等于P[0~j-1]的最长真后缀。
这里最长的意思举个例子如下:虽然下图有2种方法把模式串分成3部分,但是我们取k=3
4. 为什么一定是右移j-k
但我们在 j 这个位置发现失配的时候(S[i]!=P[j]), 模式串可以右移1,2,..,j-1。那为什么一定是j-k呢。 下图可以帮助理解
5. 求next
再说下next[j]=k是把P[0]~P[j-1]分成了3个部分: 但是2这个部分是可以为空的:
如果2为空,1跟1相交也是可以的:
注:next[0]= -1, -1 表示S[i]与P[0]不需要比较了,直接比较S[i+1]跟P[0].
按照上面的规则:我们来看几个例子:
求next的算法,虽然我们很容易能看出next的值,但是如果从找最长相等的真前缀跟真后缀这个角度出发的话,求出next数组的复杂度可能比朴素匹配的复杂度还高。实际上的求法是一个迭代的过程。即: next[j] = f(next[j-1]), 因为next[0]=-1已知道,f为某种规则。
算法:
1. 已知next[j]=k, 如果P[j]=P[k], 则next[j+1]=next[j]+1=k+1。原因如下图:
2. 如果P[j]!=P[k]怎么办呢, 只要一直求一个k(k = next[k]), 使得p[k]=p[j], 或者直到k=-1。如图:
上诉过程很容易翻译成代码:
void get_next_tmp(char *p, int next[]){ int l=strlen(p); int i=0,k=-1; next[0]=-1; while(i<l-1) { if(k==-1 || p[i]==p[k]) { i++;k++; next[i]=k; } else k=next[k]; }}
6. 改进next
上诉的next还可以再改进,能改进的原因其实也很简单。因为我们再求next[j]的时候只考虑了0~j-1串的情况,至于P[j]到底是什么字符完全没有考虑,实际上 j 这个位置的信息能让我们把模式串更加往右移一些。
1. 先看图吧:
这个图的意思是这样的,首先P[j]!=S[i], 按照上述的规则算出来next[j]=k, 有P[k]=p[j]!=S[i], 所以完全没必要把j回溯到k, 我们继续求K' = next[k], 这时候有P[K']=S[i]='a'. 所以方法是:一直求k=next[k], 直到k=-1或者P[K]!=p[j], 实际上我们每一个next[j]都是这样 求出来的,所以只需要一步k=next[k]即可。
用上述的规则把前面的4个例子改下如下:
好了,其实代码也是很直观的:
void get_next(char *P, int n[]){ int i=0,k=-1; int l=strlen(P); n[0]=-1; while(i<l-1) { if(k==-1 || P[i]==P[k]){ ++i;++k; if(P[i]!=P[k]) n[i]=k; else n[i]=n[k]; } else k=n[k]; }}
7. kmp匹配程序
最后kmp_index函数:
int kmp_index(char *s, char *p){ if(s==NULL || p==NULL) return -1; int pl = strlen(p); if(pl==0) return -1; int *n=(int *)malloc(sizeof(int)*pl); get_next(p,n); int i=0,j=0; while(s[i]!='\0' && p[j]!='\0') { if(s[i]==p[j]) { i++;j++; }else { if(n[j]!=-1){ j=n[j]; }else{ j=0; ++i; } } } free(n); if(p[j]=='\0') return i-j; else return -1;}
- KMP算法详解 【KMP】
- 【KMP】KMP算法模板
- KMP hihoCoder1015 KMP算法
- kmp算法
- KMP算法
- KMP算法
- KMP算法
- KMP算法
- KMP 算法
- kmp算法
- KMP算法
- kmp算法
- KMP算法
- KMP算法
- kmp算法
- kmp算法
- KMP算法
- KMP算法
- 最近用htmlunit做网络爬虫 遇到拿不到初始化js加载的数据的问题 最近解决了 写个简单的例子
- Linux平台下线程池的原理及实现
- 破除BC上的制度限制
- POI导出Excel 含一维码图片
- MyBatisMap传入数据错误解决
- kmp算法
- libnet编译,windows/Linux
- Poj 1635 Subway tree systems (树的最小表示)
- Android的init过程详解
- 数据库Error 1045(28000) Access Denied for user 'root'@'localhost' 错误
- 简单有效-禁止其他人到你电脑上用QQ
- 动态的获取和设置ImageView的宽度和高度
- linux下的二进制工具(反编译工具)
- ios UIImageView 中的image显示部分