KMP算法

来源:互联网 发布:淘宝花草茶 那么便宜 编辑:程序博客网 时间:2024/06/05 11:51
KMP算法

什么是KMP算法

  • KMP算法是一种改进的单字符串匹配算法。
  • 全名:克努特莫里斯普拉特操作(简称KMP算法)
  • KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。
  • 具体实现就是实现一个next()函数,函数本身包含了模式串的局部匹配信息。

KMP算法的实例

  • 有这样一个字符串:BBC ABCDAB ABCDABCDABDE
  • 我想知道,里面是否包含另一个字符串:ABCDABD?

KMP算法的思路

  • 首先,字符串"BBC ABCDAB ABCDABCDABDE"的第一个字符与搜索词"ABCDABD"的第一个字符,进行比较。
  • 因为B与A不匹配,所以搜索词后移一位。
  • 因为B与A不匹配,搜索词再往后移就这样,直到字符串有一个字符,与搜索词的第一个字符相同为止。
  • 接着比较字符串和搜索词的下一个字符.还是相同。
  • 直到字符串有一个字符,与搜索词对应的字符不相同为止。

  • 这时,最自然的反应是,将搜索词整个后移一位,再从头逐个比较。
  • 这样做虽然可行,但是效率很差,因为你要把"搜索位置"移到已经比较过的位置,重比一遍。
  • 那么我们想什么办法来解决这一问题呢?
  • 一个基本事实是,当空格与D不匹配时,你其实知道前面六个字符是"ABCDAB"。
  • KMP算法的想法是,设法利用这个已知信息,不要把"搜索位置"移回已经比较过的位置,继续把它向后移,这样就提高了效率。
  • 可以针对搜索词,算出一张《NEXT值表》,即失败指针。这张表是如何产生的,等下再介绍,这里只要会用就可以了。
  • 已知空格与D不匹配时,前面六个字符"ABCDAB"是匹配的。

  • 查表可知,字符D对应的“NEXT值"为2,因此按照下面的公式算出向后移动的位数:
  • 移动位数 = 已匹配的字符数 - 对应的NEXT值
  • 因为 6 - 2 等于4,所以将搜索词向后移动4位。
  • 因为空格与C不匹配,搜索词还要继续往后移。这时,已匹配的字符数为2("AB"),C对应的“NEXT值"为0。所以,移动位数 = 2 - 0,结果为 2,于是将搜索词向后移2位。
  • 因为空格与A不匹配,0-(-1)=1,所以继续后移一位。
  • 逐位比较,直到发现C与D不匹配。


  • 于是,移动位数 = 6 - 2,继续将搜索词向后移动4位。
  • 逐位比较,直到搜索词的最后一位,发现完全匹配,于是搜索完成。如果还要继续搜索(即找出全部匹配),移动位数 = 7 - 0,再将搜索词向后移动7位,这里就不再重复了。


  • 下面介绍《NEXT值表》是如何产生的。
    • 第一位的next值必定为-1; 
    • 计算第n个字符的NEXT值:
      1. 查看第n-1个字符对应NEXT值,设为a;
      2. 判断a是否为-1,若为-1,则第n个字符next值为0
      3. 若不为-1,将第n-1个字符与第a个字符比较
      4. 如果相同,第n个字符对应的NEXT值为a+1
      5. 如果不同,令a等于第a个字符的NEXT值,执行第第2步。

KMP模板代码

#include <stdio.h>#include <string.h>using namespace std;int next[10001];char str0[1000001];char str1[10001];void get_next(char *s){    int i=0,j=-1;    next[0]=-1;    int len=strlen(s);    while(i<len)    {        if(j==-1 || s[i]==s[j])        {            i++;            j++;            next[i]=j;        }        else            j=next[j];    }}int kmp(char *s,char *t){    int len1=strlen(s),len2=strlen(t);    int i=0,j=0,cnt=0;    while(i<len1)    {        if(j==-1 ||s[i]==t[j])        {            i++;            j++;        }        else            j=next[j];        if(j==len2)        {            cnt++;            j=next[j];        }    }    return cnt;}int main(){    int n;    scanf("%d\n",&n);    while(n--)    {        gets(str1);        gets(str0);        get_next(str1);        printf("%d\n",kmp(str0,str1));    }    return 0;}


1 0
原创粉丝点击