`

KMP算法详解及各种应用

阅读更多

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]
3) 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,继续进行比较。
代码实现如下:

[cpp]view plaincopy
 
  1. intKMPMatch(char*s,char*p)
  2. {
  3. intnext[100];
  4. inti,j;
  5. i=0;
  6. j=0;
  7. getNext(p,next);
  8. while(i<strlen(s))
  9. {
  10. if(j==-1||s[i]==p[j])
  11. {
  12. i++;
  13. j++;
  14. }
  15. else
  16. {
  17. j=next[j];//消除了指针i的回溯
  18. }
  19. if(j==strlen(p))
  20. returni-strlen(p);
  21. }
  22. return-1;
  23. }

因此KMP算法的关键在于求算next[]数组的值,即求算模式串每个位置处的最长后缀与前缀相同的长度, 而求算next[]数组的值有两种思路,第一种思路是用递推的思想去求算,还有一种就是直接去求解。
1、按照递推的思想:
根据定义next[0]=-1,假设next[j]=k, 即P[0...k-1]==P[j-k,j-1]
1)若P[j]==P[k],则有P[0..k]==P[j-k,j],很显然,next[j+1]=next[j]+1=k+1;
2)若P[j]!=P[k],则可以把其看做模式匹配的问题,即匹配失败的时候,k值如何移动,显然k=next[k]。
因此可以这样去实现:

[cpp]view plaincopy
 
  1. voidgetNext(char*p,int*next)
  2. {
  3. intj,k;
  4. next[0]=-1;
  5. j=0;
  6. k=-1;
  7. while(j<strlen(p)-1)
  8. {
  9. if(k==-1||p[j]==p[k])//匹配的情况下,p[j]==p[k]
  10. {
  11. j++;
  12. k++;
  13. next[j]=k;
  14. }
  15. else//p[j]!=p[k]
  16. k=next[k];
  17. }
  18. }

2、直接求解方法

[cpp]view plaincopy
 
  1. voidgetNext(char*p,int*next)
  2. {
  3. inti,j,temp;
  4. for(i=0;i<strlen(p);++i)
  5. {
  6. if(i==0)
  7. {
  8. next[i]=-1;//next[0]=-1
  9. }
  10. elseif(i==1)
  11. {
  12. next[i]=0;//next[1]=0
  13. }
  14. else
  15. {
  16. temp=i-1;
  17. for(j=temp;j>0;--j)
  18. {
  19. if(equals(p,i,j))
  20. {
  21. next[i]=j;//找到最大的k值
  22. break;
  23. }
  24. }
  25. if(j==0)
  26. next[i]=0;
  27. }
  28. }
  29. }
  30. boolequals(char*p,inti,intj)//判断p[0...j-1]与p[i-j...i-1]是否相等
  31. {
  32. intk=0;
  33. ints=i-j;
  34. for(;k<=j-1&&s<=i-1;k++,s++)
  35. {
  36. if(p[k]!=p[s])
  37. returnfalse;
  38. }
  39. returntrue;
  40. }

http://poj.org/problem?id=2406

 

给定一个字符串,问最多是多少个相同子串不重叠连接构成。

KMP的next数组应用。这里主要是如何判断是否有这样的子串,和子串的个数。

若为abababa,显然除其本身外,没有子串满足条件。而分析其next数组,next[7] = 5,next[5] = 3,next[3] = 1,即str[2..7]可由ba子串连接构成,那怎么否定这样的情况呢?很简单,若该子串满足条件,则len%sublen必为0。sunlen可由上面的分析得到为len-next[len]。

因为子串是首尾相接,len/sublen即为substr的个数。

若L%(L-next[L])==0,n = L/(L-next[L]),else n = 1

[cpp]view plaincopy
 
  1. #include<iostream>
  2. #include<cstdio>
  3. usingnamespacestd;
  4. charpattern[1000002];
  5. intnext[1000002];
  6. /*
  7. kmp算法,需要首先求出模式串的next函数值
  8. next[j]=k,说明p0pk-1==pj-kpj-1,也就是说k为其前面相等串的长度
  9. */
  10. voidget_nextval(constchar*pattern)
  11. {
  12. inti=0,j=-1;
  13. next[0]=-1;
  14. while(pattern[i]!='\0')
  15. {
  16. if(j==-1||pattern[i]==pattern[j])//pattern[i]表示后缀的单个字符,pattern[j]表示前缀的单个字符
  17. {
  18. ++i;
  19. ++j;
  20. if(pattern[i]!=pattern[j])
  21. next[i]=j;
  22. else
  23. next[i]=next[j];
  24. }
  25. else
  26. j=next[j];//若j值不相同,则j值回溯
  27. }
  28. }//get_nextval
  29. intmain(void)
  30. {
  31. intlen;
  32. while(scanf("%s",pattern)!=EOF)
  33. {
  34. if(pattern[0]=='.')
  35. break;
  36. len=strlen(pattern);
  37. get_nextval(pattern);
  38. if(len%(len-next[len])==0)
  39. printf("%d\n",len/(len-next[len]));
  40. else
  41. printf("1\n");
  42. }
  43. return0;
  44. }

http://poj.org/problem?id=1961

大意:
定义字符串A,若A最多由n个相同字串s连接而成,则A=s^n,如"aaa" = "a"^3,"abab" = "ab"^2
"ababa" = "ababa"^1

给出一个字符串A,求该字符串的所有前缀中有多少个前缀SA= s^n(n>1)
输出符合条件的前缀长度及其对应的n

如aaa
前缀aa的长度为2,由2个'a'组成
前缀aaa的长度为3,由3个"a"组成

分析:KMP
若某一长度L的前缀符合上诉条件,则
1.next[L]!=0(next[L]=0时字串为原串,不符合条件)
2.L%(L-next[L])==0(此时字串的长度为L/next[L])

对于2:有str[0]....str[next[L]-1]=str[L-next[L]-1]...str[L-1]
=》str[L-next[L]-1] = str[L-next[L]-1+L-next[L]-1] = str[2*(L-next[L]-1)];
假设S = L-next[L]-1;则有str[0]=str[s]=str[2*s]=str[3*s]...str[k*s],对于所有i%s==0,均有s[i]=s[0]
同理,str[1]=str[s+1]=str[2*s+1]....
str[j]=str[s+j]=str[2*s+j]....
综上,若L%S==0,则可得L为str[0]...str[s-1]的相同字串组成,
总长度为L,其中字串长度SL = s-0+1=L-next[L],循环次数为L/SL
故对于所有大于1的前缀,只要其符合上述条件,即为答案之一

[cpp]view plaincopy
 
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. usingnamespacestd;
  5. charpattern[1000002];
  6. intnext[1000002];
  7. /*
  8. kmp算法,需要首先求出模式串的next函数值
  9. next[j]=k,说明p0pk-1==pj-kpj-1,也就是说k为其前面相等串的长度
  10. */
  11. voidget_nextval(constchar*pattern)
  12. {
  13. inti=0,j=-1;
  14. next[0]=-1;
  15. while(pattern[i]!='\0')
  16. {
  17. if(j==-1||pattern[i]==pattern[j])
  18. {
  19. ++i;
  20. ++j;
  21. next[i]=j;
  22. }
  23. else
  24. j=next[j];
  25. }
  26. }//get_nextval
  27. intmain(void)
  28. {
  29. inti,len,n,j=1;
  30. while(scanf("%d",&n)!=EOF)
  31. {
  32. if(!n)
  33. break;
  34. scanf("%s",pattern);
  35. len=strlen(pattern);
  36. get_nextval(pattern);
  37. printf("Testcase#%d\n",j++);
  38. for(i=2;i<=len;i++)
  39. {
  40. if(i%(i-next[i])==0&&i/(i-next[i])>1)
  41. printf("%d%d\n",i,i/(i-next[i]));
  42. }
  43. printf("\n");
  44. }
  45. return0;
  46. }

http://poj.org/problem?id=2752
大意:
给出一个字符串A,求A有多少个前缀同时也是后缀,从小到大输出这些前缀的长度。

分析:KMP
对于长度为len的字符串,由next的定义知:
A[0]A[1]...A[next[len]-1]=A[len-next[len]]...A[len-1]此时A[0]A[1]...A[next[len]-1]为一个符合条件的前缀
有A[0]A[1]....A[next[next[len]]-1] = A[len-next[next[len] - next[next[len]]]...A[next[len]-1],故A[0]A[1]....A[next[next[len]]-1]也是一个符合条件的前缀
故从len=>next[len]=>next[next[len]] ....=>直到某个next[]为0均为合法答案,注意当首位单词相同时,也为答案。

[cpp]view plaincopy
 
  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<vector>
  5. usingnamespacestd;
  6. charpattern[400002];
  7. intnext[400002];
  8. /*
  9. kmp算法,需要首先求出模式串的next函数值
  10. next[j]=k,说明p0pk-1==pj-kpj-1,也就是说k为其前面相等串的长度
  11. */
  12. voidget_nextval(constchar*pattern)
  13. {
  14. inti=0,j=-1;
  15. next[0]=-1;
  16. while(pattern[i]!='\0')
  17. {
  18. if(j==-1||pattern[i]==pattern[j])
  19. {
  20. ++i;
  21. ++j;
  22. next[i]=j;
  23. }
  24. else
  25. j=next[j];
  26. }
  27. }//get_nextval
  28. intmain(void)
  29. {
  30. inti,len,n;
  31. vector<int>ans;
  32. while(scanf("%s",pattern)!=EOF)
  33. {
  34. ans.clear();
  35. len=strlen(pattern);
  36. get_nextval(pattern);
  37. n=len;
  38. while(n)
  39. {
  40. ans.push_back(n);
  41. n=next[n];
  42. }
  43. if(pattern[0]==pattern[n-1])//首部、尾部字符相同
  44. ans.push_back(1);
  45. i=ans.size()-1;
  46. for(;i>0;i--)
  47. printf("%d",ans[i]);
  48. printf("%d\n",ans[0]);
  49. }
  50. return0;
  51. }
分享到:
评论

相关推荐

    KMP算法详解 KMP算法详解

    在实际应用KMP算法时,首先计算出模式串B的P[j]数组,然后进行字符串匹配。在匹配过程中,如果A[i]和B[j]匹配,则i和j都加1;如果不匹配,j根据P[j]回溯,i不变,直到找到匹配的下一个位置或j回溯到0。 总的来说,...

    KMP算法详解KMP算法详解

    KMP 算法详解 KMP 算法是字符串模式匹配的一种高效算法,解决了字符串中模式匹配的问题。该算法可以在 O(m+n) 的时间复杂度内完成字符串模式匹配。 简单匹配算法 简单匹配算法的思想是直截了当的,将主串 S 中...

    kmp算法详解

    "kmp算法详解" KMP 字符串模式匹配算法是高效的字符串匹配算法,时间复杂度为 O(m+n),其中 m 和 n 分别是主串和模式串的长度。KMP 算法的核心思想是利用已经得到的部分匹配信息来进行后面的匹配过程。 KMP 算法的...

    KMP算法详解0.0.doc

    KMP算法的核心在于构建一个称为“部分匹配表”或“next数组”,用于存储模式串中每个位置的前缀和后缀的最大公共长度。 在构建next数组的过程中,我们需要遵循以下两个条件: 1. 当比较到某个位置时,如果模式串的...

    字符串模式匹配KMP算法详解.doc

    ### 字符串模式匹配KMP算法详解 #### 一、引言 在计算机科学领域,字符串模式匹配是一项基本且重要的任务。它涉及到在一个较大的文本字符串(通常称为“主串”或“目标串”)中寻找一个较小的字符串(称为“模式串...

    关于数据结构中的kmp算法详解

    ### KMP算法详解:原理与应用 #### 引言 KMP算法,全称为Knuth-Morris-Pratt算法,是一种高效的字符串匹配算法,由Donald E. Knuth、James H. Morris以及Vaughan Pratt三位计算机科学家共同提出。相较于传统的模式...

    KMP算法详解.doc

    "KMP算法详解" 一、KMP字符串模式匹配算法 KMP字符串模式匹配算法是一种高效的字符串模式匹配算法,能够在一个字符串中快速地定位另一个字符串。该算法的时间复杂度为O(m+n),远远优于简单匹配算法的时间复杂度O(m...

    KMP算法详解-彻底清楚了.pdf

    《KMP算法详解》 KMP(Knuth-Morris-Pratt)算法是一种在字符串中查找子串出现位置的高效算法,由Donald Knuth、James H. Morris和 Vaughan Pratt共同提出。该算法避免了在匹配过程中对已匹配部分的重新比较,显著...

    KMP算法:高效字符串匹配算法详解

    kmp算法内容概况: 本文将介绍一种名为KMP的字符串匹配算法。...KMP算法可以优化查找操作,提高应用程序的性能。 5. 字符串匹配问题解决者:在解决字符串匹配问题时,需要快速找到一个字符串在另一个字符串中的位

    严蔚敏 数据结构 kmp算法详解.pdf

    KMP算法是一种高效的字符...KMP算法在计算机科学中有着广泛的应用,包括文本编辑器中的查找功能、搜索引擎中的关键词索引、生物信息学中的序列匹配等。掌握KMP算法的原理和实现,对于处理字符串相关问题是非常重要的。

    Matrix67_ KMP算法详解1

    KMP算法,全称为Knuth-Morris-Pratt算法,是一种高效的字符串匹配算法,由三位同名研究人员提出。它的核心思想在于避免在匹配过程中...在实际应用中,尤其是在处理大量字符串匹配问题时,KMP算法展现出显著的性能优势。

    KMP算法~~完整的

    ### KMP算法详解 #### 简介 KMP算法,全称为Knuth-Morris-Pratt算法,是一种高效的字符串模式匹配算法。相比于简单的模式匹配算法,KMP算法能够显著提高搜索速度,尤其是在处理大规模文本数据时优势明显。本文将...

    2.KMP算法:高效字符串匹配算法详解

    kmp算法内容概况: 本文将介绍一种名为KMP的字符串匹配算法。...KMP算法可以优化查找操作,提高应用程序的性能。 5. 字符串匹配问题解决者:在解决字符串匹配问题时,需要快速找到一个字符串在另一个字符串中的位

    kmp算法概述、原理及应用详解.pdf

    通过利用已经匹配过的信息来优化匹配过程,KMP算法不仅能够显著提高字符串匹配的效率,还能应用于多种实际问题中。尽管如此,在面对更复杂的字符串处理需求时,可能还需要结合其他算法和技术来实现更高效的解决方案...

    kmp算法 编程

    ### KMP算法详解:一种高效的字符串匹配方法 #### 引言 KMP算法(Knuth-Morris-Pratt算法)是一种在计算机科学中用于解决字符串匹配问题的高效算法。相较于朴素的字符串匹配算法,KMP算法能够显著减少不必要的比较...

    36KMP算法.zip

    《36KMP算法详解与应用》 36KMP算法,全称为“Knuth-Morris-Pratt”算法,是一种在字符串中高效查找子串出现位置的算法,由D.E. Knuth、V. Morris和J.H. Pratt三位学者在1970年代提出。该算法的核心在于构造一个...

    KMP.rar_KMP_KMP算法_串 KMP算法_字符串匹配

    《KMP算法详解——高效字符串匹配的秘密》 在信息技术领域,字符串处理是极其常见的操作,尤其是在文本分析、数据挖掘和模式识别中。其中,字符串匹配是核心问题之一,而KMP(Knuth-Morris-Pratt)算法正是解决这一...

    数据结构KMP算法配图详解

    在这个"数据结构KMP算法配图详解"中,我们将深入探讨该算法的原理、实现以及它在C语言和Java中的具体应用。 1. KMP算法概述: KMP算法由D.E. Knuth、V.R. Morris和J.H. Pratt于1970年提出,它的核心思想是避免对已...

Global site tag (gtag.js) - Google Analytics