当前位置:   article > 正文

【数据结构】KMP算法_数据结构kmp算法

数据结构kmp算法

简介

KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)(资料来源百度百科)

next[i]数组

next[i]数组是什么

next[i]数组是一个用来储存前i个字符组成的字符串的前缀和后缀相同的个数的的函数

 在字符串”ABCABCACD"中,

截至为“ABCAB"除去我们所在的字符B剩下的串为

字符串"ABCA"中前缀和尾缀相同的仅为"A"

因此next[4]=1;

截至为“ABCABC"除去我们所在的字符C剩下的串为

 数组"ABCAB"中前缀和尾缀相同的为"AB"

因此next[5]=2;

怎么求next[i]数组

  1. void getnext(int next[],string *S)
  2. {
  3. int i = 0,j=-1;
  4. next[0] = -1;//仅是我们将其定义为next[0]为-1
  5. while (i < S->length)
  6. {
  7. if (j == -1 || S->str[i] == S->str[j])
  8. {
  9. i++;
  10. j++;
  11. next[i] = j;
  12. }
  13. else
  14. j = next[j];
  15. }
  16. }

我刚开始看的时候对求next[i]的两句话不是很理解

为什么我们要有j==-1

if (j == -1 || S->str[i] == S->str[j])

 另一句是为啥是j=next[j] (重点)

j = next[j];

首先我们来讲第二句

在"ABCABCABD"中,当我们头缀尾缀匹配到B时,匹配上的已经有"ABCAB"

再接下去我们要进行匹配的是头缀的'C'和尾缀的'D',这时我们就已经匹配不上了

那此时进行j=next[j]即是令j=next[5]=2,仔细想想我们的'D'的前面的字符串有几个和头缀相同

就是next[5]个

所以我们的匹配就会接着把移到前面两个已经匹配了

如果我们的接下来的能够匹配上

假如:

 这样我们就可以接着匹配了

但要是没继续匹配上

 此时我们再去匹配会发现还是不匹配

所以再去看前面的头尾缀有相同的否就发现没有相同的,那么就会使得我们的匹配把j赋值为next[2]即是0

这时就是把'D'和首字符'A'进行比较

此时发现还是匹配不上那么就把j=next[0]=-1;

再回到

if (j == -1 || S->str[i] == S->str[j])

我们把这个这个拆开再去看就能够十分的明了了

  1. if (j == -1 )
  2. {
  3. i++;
  4. j++;
  5. next[i] = j;
  6. }
  7. else if ( S->str[i] == S->str[j])
  8. {
  9. i++;
  10. j++;
  11. next[i] = j;
  12. }

else if我们能够很明了的了解到

当两个相同的时候就可以移步到下一个这样他的去除自身的头尾缀最大就可以记录下来,接着包括他自身再次进行一个匹配,如果匹配上了则进入下个接着匹配,否则则进入else 进行j=next[j]的操作

那么j==-1

第一

就是为了我们最开始的时候在j=-1和i=0能够先进入j++和i++从而对字符串的第1个(S->str[0])和第2个(S->str[1])进行对比

第二是为了在我们在我们的当前字符(这里是'D')和首字符匹配不上的时候,那么就把当前字符的next[i]赋值为0,就是没有和前面有任何匹配,同时呢,再把我们要匹配的字符往下面再移动一个和第一个字符进行匹配

由于上面的几个的都是相同的表示所以我们就把j==-1和S->str[i] == S->str[j]合并起来了

KMP算法

我们先给出一段代码

  1. int KMP(string* T, string* S)
  2. {
  3. int i = 0, j = 0;
  4. int next[255];
  5. getnext(&next,S);
  6. while (i < T->length)
  7. {
  8. if (j == -1 || T->str[i] == S->str[j])
  9. {
  10. i++;
  11. j++;
  12. if (j == S->length)
  13. return i - j+1;
  14. }
  15. else
  16. j = next[j];
  17. }
  18. }

首先

int i = 0, j = 0;

这边给j赋值为0就是为了最开始就进行S串和T串的匹配,如果匹配不上就把j赋值到-1,为了把我们要匹配的字符往下面再移动一个和第一个字符进行匹配(类同next的求法)

  1. if (j == S->length)
  2. return i - j+1;

用来返回最先匹配到的字符串的首的位置

  1. j = next[j];

和next的求法里的意思是一样的当没匹配上的时候就会进行往前去寻找有没有匹配上的个数有则接着匹配,没有则回到最开头进行下个字符的匹配

最后附上全部的代码

  1. #include<stdio.h>
  2. #include<string.h>
  3. typedef struct
  4. {
  5. char str[255];
  6. int length;
  7. }string;
  8. int KMP(string* T, string* S);
  9. void getnext(int next[],string *S);
  10. int main()
  11. {
  12. int n;
  13. string T,S;
  14. gets(T.str);
  15. gets(S.str);
  16. T.length = strlen(T.str);
  17. S.length = strlen(S.str);
  18. n=KMP(&T,&S);
  19. printf("%d", n);
  20. return 0;
  21. }
  22. int KMP(string* T, string* S)
  23. {
  24. int i = 0, j = 0;
  25. int next[255];
  26. getnext(next,S);
  27. while (i < T->length)
  28. {
  29. if (j == -1 || T->str[i] == S->str[j])
  30. {
  31. i++;
  32. j++;
  33. if (j == S->length)
  34. return i - j+1;
  35. }
  36. else
  37. j = next[j];
  38. }
  39. }
  40. void getnext(int next[],string *S)
  41. {
  42. int i = 0,j=-1;
  43. next[0] = -1;
  44. while (i < S->length)
  45. {
  46. if (j == -1 || S->str[i] == S->str[j])
  47. {
  48. i++;
  49. j++;
  50. next[i] = j;
  51. }
  52. else
  53. j = next[j];
  54. }
  55. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/662092
推荐阅读
相关标签
  

闽ICP备14008679号