鱼C论坛

 找回密码
 立即注册
查看: 3784|回复: 14

[技术交流] KMP解析

[复制链接]
发表于 2013-6-10 21:49:00 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 Sharing 于 2013-6-10 21:52 编辑

原文出处http://blog.csdn.net/sharing_li/article/details/8822946
先看字符串S“abcdex”和T"abcdx",比较的时候,两字符串都从下标0开始比较,直到S[4] = 'e' != T[4] = 'x'。然后S从下标1开始,T从下标0开始继续比较。我们可以发现,这是没必要的,在T中,T[0]和T[1],T[0]和T[2],T[0]和T[3]都不相等,因为T[1] = S[1],T[2] = S[2],T[3] = S[3],所以T[0]和S[1],S[2],S[3]都不相等,所以就没有必要和它们比较,直接从S的下标4开始,T的下标0开始比较。我们还会发现,比较的过程中,S的下标 i 不会后退,i 要么原地踏步,要么向前进,但是T的下标 j 是可以后退、原地踏步和前进的。所以我们在比较的过程中,当S != T[j] 时,i 不后退,将 j 后退到合适的位置。我们用next数组保存 j 后退的适当位置。j = next[j] 就表示但串T第 j 个字符与S对应字符不想等时,下标 j 退到适当的位置。那我们怎么确定j要后退到哪里呢?我们先来看看下面一些求next数组的例子:
1.T = "abcdex"
j          :   0    1     2     3    4    5

T         :   a    b     c     d    e     x
next[j] : -1    0     0     0    0     0


1)j = 0时:一开始就不相等,则j无路可退,我们定义next[0] = -1,-1表示 j = 0是j无路可退;
2)j = 1时:下标0到 j - 1所组成的字符串是“a”,j退到下标0的位置,next[1] = 0;
3)j = 2时:下标0到 j - 1所组成的字符串是“abc”,j还是退到下标0的位置,next[2] = 0;
4)同理
5)同理

6)同理
2.T = "abcabx"

j          :   0    1     2     3    4    5

T         :   a    b     c     a    b     x

next[j] : -1    0     0     0    1     2

1)j = 0时,同理next[0] = -1;
2)j = 1时,同理next[1] = 0;
3)同理
4)同理
5)j = 4时,下标0到 j - 1所组成的字符串是“abca”,可以发现前缀T[0]和后缀T[3]相等,我们之后都用绿色表示前缀,红色表示后缀,此时 j 应该后退到 j = 1处,即next[4] = 1。想一想,为什么?因为T和S比较时,S[3] = a,S[4] = b,而T[0]和T[3]相等,T[3]又和S[3]相等,所以S[3] == T[0]。则下次比较时,T从下标1开始。
6)j = 5时,下标0到 j - 1所组成的字符串是“abcab”,可以发现前缀T[0~1] = "ab" = 后缀 T[3~4],所以 j 后退到 j  = 2处,即next[5] = 2;
可以发现,j 后退的位置为前缀和后缀的相似度,再看两个例子:
3. T = “ababaaaba”

j          :   0    1     2     3    4     5     6     7    8

T         :   a    b     a     b    a    a     a     b    a

next[j] : -1    0      0    1    2     3     1     1    2

1)j = 0时,next[0] = -1;
2)j = 1时,next[1] = 0;
3)同理;
4)j = 3时,下标0到 j - 1所组成的字符串是“aba”,前缀为a,后缀为a,相似度为1,所以next[3] = 1;

5)j = 4时,下标0到 j - 1所组成的字符串是“abab”,前缀为ab,后缀为ab,相似度为2,next[4] = 2;6)j = 5时,下标0到 j - 1所组成的字符串是“ababa”,前缀为aba,后缀为aba,相似度为3,next[5] = 3;
7)j = 6时,下标0到 j - 1所组成的字符串是“ababaa”,前缀为a,后缀为a,相似度为1,next[6] = 1;
之后同理。

算法实现如下:
  1. #include<stdio.h>
  2. #include<string.h>
  3. void GetNext(char * T,int * next)
  4. {
  5. int i = 0,j = -1;
  6. next[0] = -1;
  7. while(i < strlen(T))
  8. {
  9.   if (j == -1 || T[i] == T[j])//T[i]表示后缀的单个字符,T[j]表示前缀的单个字符
  10.   {
  11.    i++;
  12.    j++;
  13.    next[i] = j;
  14.   }
  15.   else
  16.   {
  17.    j = next[j];//若字符串不同,则j值后退
  18.   }
  19. }
  20. }
  21. int KMP(char * S,char * T)
  22. {
  23. int i = 0;//i为主串S当前位置下标
  24. int j = 0;//j为字串T当前位置下标
  25. int next[255];//为什么是255?想想char的范围
  26. int S_len = strlen(S),T_len = strlen(T);
  27. GetNext(T,next);//得到T的next数组
  28. while (i < S_len && j < T_len)
  29. {
  30.   if (j == -1 || S[i] == T[j])//两字符相等则继续比较
  31.   {
  32.    i++;
  33.    j++;
  34.   }
  35.   else//若不相等,则i不后退,j后退到合适的位置
  36.   {
  37.    j = next[j];
  38.   }
  39. }
  40. if (j == strlen(T))//若存在
  41.   return 1;
  42. else
  43.   return 0;
  44. }
  45. int main()
  46. {
  47. char * s = "abcdaefg", * t = "bcd";
  48. if (!KMP(s,t))
  49. {
  50.   printf("不存在!\n");
  51. }
  52. else
  53. {
  54.   printf("存在!\n");
  55. }
  56. return 0;
  57. }
复制代码
接下来,想想KMP算法是否可以改进?看看一个例子:S = "aaaabcde" ,T = “aaaaax”,T的next数组值为{-1,0,1,2,3,4},但S[4] != T[4]时,j = next[4] =3,此时S[4] != T[3],j = next[3] = 2,此时S[4] != T[2],知道j = 0。我们可以发现,其实我们可以直接把 j 退到 0,减少一些不必要的计算,修改GetNext函数如下:
  1. void GetNext(char * T,int * next)
  2. {
  3. int i,j;
  4. i = 0;
  5. j = -1;
  6. next[0] = -1;
  7. while(i < strlen(T))
  8. {
  9.   if (j == -1 || T[i] == T[j])//T[i]表示后缀的单个字符,T[j]表示前缀的单个字符
  10.   {
  11.    i++;
  12.    j++;
  13.    if (T[i] != T[j])//若当前字符与前面一个字符不相等
  14.     next[i] = j;
  15.    else
  16.     next[i] = next[j];
  17.    next[i] = j;
  18.   }
  19.   else
  20.   {
  21.    j = next[j];//若字符串不同,则j值后退
  22.   }
  23. }
  24. }
复制代码
原文出处http://blog.csdn.net/sharing_li/article/details/8822946

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2013-6-10 23:34:25 | 显示全部楼层
无回帖,不论坛,这才是人道。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-7-24 20:33:14 | 显示全部楼层
楼主ing……
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-8-9 10:19:21 | 显示全部楼层
值得学习···
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-8-24 10:34:04 | 显示全部楼层
修改Next后的 next[i] = j;不就重复了吗?    4始终实行, 那 1 跟 3那不就米意义了?

0  if (T[i] != T[j])//若当前字符与前面一个字符不相等
1  next[i] = j;
2  else
3  next[i] = next[j];
4  next[i] = j;
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-8-24 11:38:17 | 显示全部楼层
`````````````````````````
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-9-8 20:51:02 | 显示全部楼层

我也不明白这里,不要第四步,得出的next值又好像是错的,不明白了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-9-16 20:44:56 | 显示全部楼层
楼主ing……
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-11-17 08:55:35 | 显示全部楼层
好文章转过来方便自己看,,,,,,
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-11-17 10:31:11 | 显示全部楼层
马克 !回头学了KMP 回来看看
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-11-22 23:25:53 | 显示全部楼层
求解释,我也遇到同样的问题了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-11-26 13:16:45 | 显示全部楼层
我是来打酱油的..!~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-11-27 16:18:55 | 显示全部楼层
我是来打酱油的..!~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2013-12-3 10:38:48 | 显示全部楼层
看毛片算法:lol:
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-8-16 22:06:02 | 显示全部楼层
谢谢楼主分享!努力学习中。这个算法理解以及手工解题(next[]、nextval[])太简单了,只是难于用代码表示。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-19 15:46

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表