赞
踩
最近在学算法,在看KMP算法时总感觉不怎么明白,网上也看了那么多关于KMP算法的介绍,还是没怎么看懂,最后索性按照自己的理解,然后写出代码,主要是改变了next求解的定义。看了其他大牛在说KMP算法前都先说下BF算法,我也跟随前辈,先说下BF算法。
一.BF算法
BF算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串P的第一个字符进行匹配,若相等,则继续比较S的第二个字符和P的第二个字符;若不相等,则比较S的第二个字符和P的第一个字符,依次比较下去,直到得出最后的匹配结果。
举例说明:
S: ababcababa
P: ababa
BF算法匹配的步骤如下
i=0 i=1 i=2 i=3 i=4
第一趟:ababcababa 第二趟:ababcababa 第三趟:ababcababa 第四趟:ababcababa 第五趟:ababcababa
ababa ababa ababa ababa ababa
j=0 j=1 j=2 j=3 j=4(i和j回溯)
i=1 i=2 i=3 i=4 i=3
第六趟:ababcababa 第七趟:ababcababa 第八趟:ababcababa 第九趟:ababcababa 第十趟:ababcababa
ababa ababa ababa ababa ababa
j=0 j=0 j=1 j=2(i和j回溯) j=0
i=4 i=5 i=6 i=7 i=8
第十一趟:ababcababa 第十二趟:ababcababa 第十三趟:ababcababa 第十四趟:ababcababa 第十五趟:ababcababa
ababa ababa ababa ababa ababa
j=0 j=0 j=1 j=2 j=3
i=9
第十六趟:ababcababa
ababa
j=4(匹配成功)
代码实现:
- int BFMatch(char *s,char *p)
- {
- int i,j;
- i=0;
- while(i<strlen(s))
- {
- j=0;
- while(s[i]==p[j]&&j<strlen(p))
- {
- i++;
- j++;
- }
- if(j==strlen(p))
- return i-strlen(p);
- i=i-j+1; //指针i回溯
- }
- return -1;
- }
二.KMP算法
KMP算法之所以叫做KMP算法是因为这个算法是由三个人共同提出来的,就取三个人名字的首字母作为该算法的名字。其实KMP算法与BF算法的区别就在于KMP算法巧妙的消除了指针i的回溯问题,只需确定下次匹配j的位置即可,使得问题的复杂度由O(mn)下降到O(m+n)。
在KMP算法中,为了确定在匹配不成功时,下次匹配时j的位置,引入了next[]数组,next[j]的值表示P[0...j-1]中最长后缀的长度等于相同字符序列的前缀。
我对对于next[]数组的定义如下:
1) next[j] = -1 j = 0
2) next[j] = max(k): 0<=k<j P[0...k-1]=P[j-k,j-1]
相信很多算法中都是这样定义的
1) next[j] = -1 j = 0
2) next[j] = max(k): 0<=k<j P[0...k-1]=P[j-k,j-1]
3) next[j] = 0 其他
我觉得3和2其实可以合并的这样,如果前缀和后缀的相似元素为0个我们就说next[j]=0,这样在程序设计上更简单些如:
P a b a b a
j 0 1 2 3 4
next -1 0 0 1 2
即next[j]=k>0时,表示P[0...k-1]=P[j-k,j-1]
因此KMP算法的思想就是:在匹配过程称,若发生不匹配的情况,如果next[j]>=0,则目标串的指针i不变,将模式串的指针j移动到next[j]的位置继续进行匹配;若next[j]=-1,则将i右移1位,并将j置0,继续进行比较。
代码实现如下:
- int KMP(char *Text,char * Pattern)
- {
- if(!Text || !Pattern || Pattern[0] == '/0' || Text[0] == '/0')
- return -1;
- int len = 0;
- const char *c = Pattern;
- while(*c++ != 0)
- {
- ++len;
- }
-
- int * next = new int[len+1];
- get_next(Pattern,next);
-
- int index = 0,i=0,j=0;
- while(Text[i] != 0 && Pattern[j] != 0)
- {
- if (Text[i] == Pattern[j])
- {
- ++i;
- ++j;
- }
- else
- {
- index += j-next[j];
- if(next[j] != -1)
- j = next[j]; //若当前匹配不成功则找到下次开始匹配的位置
- else
- {
- j=0; //如果一开始就匹配不成功则向后移
- ++i;
- }
- }
- }
-
- delete []next;
- if(Pattern[j] == 0)
- return index;
- else
- return -1;
-
-
- }
根据定义next[0]=-1,
若有next[j]=k;即表示为:
在0<=k<j-1中,当m=0:1:k俊满足P[j-1-m] = P[m],即k为重复元素最多的个数
因此可以这样去实现:
- void get_next(char * P,int * next)
- {
- int k,j;
- j = 1;
- next[0] = -1; //第一个next的值始终为-1;其实也可以写做其他标记,仅仅是表现起始位置而已
-
- while(P[j] != 0)
- {
- k = 0;
- while(k<j-1)
- {
- if(P[j-1-k] == P[k])
- {
- ++k; //在P[0,...,j-1]中找相似的部分的个数,如ababc中c之前相似的为2个,这也是KMP在下一次移动时移动的位数
- }
- else
- break; //找出其中最大的相似
- }
- next[j] = k;
- j++; //j后移,每一个位置的都找出来
- }
- }
上述是我对KMP算法的理解,主要是改变了对next求解的定义,以及代码的实现。本人最近在看KMP算法看到很多大牛关于KMP算法的解释,文中借鉴了很多,但在学习的过程中感觉next数组晦涩难懂,代码也不易看懂,所以就自己定义了next的求解,由于是菜鸟所以不知道这样的next的定义有漏洞没,欢迎指正。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。