字符串的模式匹配详解--BF算法与KMP算法

一.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;
}

  其实在上面的匹配过程中,有很多比较是多余的。在第五趟匹配失败的时候,在第六趟,i可以保持不变,j值为2。因为在前面匹配的过程中,对于串S,已知s0s1s2s3=p0p1p2p3,又因为p0!=p1!,所以第六趟的匹配是多余的。又由于p0==p2,p1==p3,所以第七趟和第八趟的匹配也是多余的。在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,继续进行比较。
代码实现如下:

int KMPMatch(char *s,char *p)
{
  int next[100];
  int i,j;
  i=0;
  j=0;
  getNext(p,next);
  while(i<strlen(s))
  {
    if(j==-1||s[i]==p[j])
    {
      i++;
      j++;
    }
    else
    {
      j=next[j];    //消除了指针i的回溯
    }
    if(j==strlen(p))
      return i-strlen(p);
  }
  return -1;
}

  因此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]。
   因此可以这样去实现:

void getNext(char *p,int *next)
{
  int j,k;
  next[0]=-1;
  j=0;
  k=-1;
  while(j<strlen(p)-1)
  {
    if(k==-1||p[j]==p[k])  //匹配的情况下,p[j]==p[k]
    {
      j++;
      k++;
      next[j]=k;
    }
    else          //p[j]!=p[k]
      k=next[k];
  }
}

2.直接求解方法

void getNext(char *p,int *next)
{
  int i,j,temp;
  for(i=0;i<strlen(p);i++)
  {
    if(i==0)
    {
      next[i]=-1;   //next[0]=-1
    }
    else if(i==1)
    {
      next[i]=0;   //next[1]=0
    }
    else
    {
      temp=i-1;
      for(j=temp;j>0;j--)
      {
        if(equals(p,i,j))
        {
          next[i]=j;  //找到最大的k值
          break;
        }
      }
      if(j==0)
        next[i]=0;
    }
  }
}
bool equals(char *p,int i,int j)   //判断p[0...j-1]与p[i-j...i-1]是否相等
{
  int k=0;
  int s=i-j;
  for(;k<=j-1&&s<=i-1;k++,s++)
  {
    if(p[k]!=p[s])
      return false;
  }
  return true;
}
(0)

相关推荐

  • KMP算法的C#实现方法

    本文实例简述了KMP算法的C#实现方法,分享给大家供大家参考.具体如下: 具体思路为:next函数求出模式串向右滑动位数,再将模式串的str的next函数值 存入数组next. 具体实现代码如下: static void GetNextVal(string str, int [] next) { int i = 0; int j = -1; next[0] = -1; while (i < str.Length - 1) { if (j == -1 || str[i] == str[j]) {

  • C++ 数据结构之kmp算法中的求Next()函数的算法

    C++ 数据结构之kmp算法中的求Next()函数的算法 实例代码: #include <iostream> using namespace std; void preKmp(char *c, int m, int Next[]) { int i=1,j=-1; Next[0]=-2; while(i<m) { if(j==-2) { Next[i]=-1; i++; j=-1; } ++j; if(i==m) return; if(c[i]==c[j]) { Next[i]=j; ++

  • KMP 算法实例详解

    KMP 算法实例详解 KMP算法,是由Knuth,Morris,Pratt共同提出的模式匹配算法,其对于任何模式和目标序列,都可以在线性时间内完成匹配查找,而不会发生退化,是一个非常优秀的模式匹配算法. 分析:KMP模板题.KMP的关键是求出next的值.先预处理出next的值.然后一遍扫过.复杂度O(m+n) 实例代码: #include<stdio.h> #include<string.h> #define N 1000005 int s[N]; int p[N]; int n

  • java 中模式匹配算法-KMP算法实例详解

    java 中模式匹配算法-KMP算法实例详解 朴素模式匹配算法的最大问题就是太低效了.于是三位前辈发表了一种KMP算法,其中三个字母分别是这三个人名的首字母大写. 简单的说,KMP算法的对于主串的当前位置不回溯.也就是说,如果主串某次比较时,当前下标为i,i之前的字符和子串对应的字符匹配,那么不要再像朴素算法那样将主串的下标回溯,比如主串为"abcababcabcabcabcabc",子串为"abcabx".第一次匹配的时候,主串1,2,3,4,5字符都和子串相应的

  • C语言中实现KMP算法的实例讲解

    一般的算法为什么这么低效呢?那是因为主串指针回溯情况过多: 主串指针如果不回溯的话,速度就会加快,那我们就会想: 如何让主串指针不回溯? KMP算法就是解决了这个问题,所以速度变得更快速了. 它是这样子的: 用一个数组:next[] 求得失配时的位置,然后保存下来. 要说清楚KMP算法,可以从朴素的模式匹配算法说起.  朴素的模式匹配算法比较容易理解,其实现如下 int Index(char s[], char p[], int pos) { int i, j, slen, plen; i =

  • JavaScript中数据结构与算法(五):经典KMP算法

    KMP算法和BM算法 KMP是前缀匹配和BM后缀匹配的经典算法,看得出来前缀匹配和后缀匹配的区别就仅仅在于比较的顺序不同 前缀匹配是指:模式串和母串的比较从左到右,模式串的移动也是从 左到右 后缀匹配是指:模式串和母串的的比较从右到左,模式串的移动从左到右. 通过上一章显而易见BF算法也是属于前缀的算法,不过就非常霸蛮的逐个匹配的效率自然不用提了O(mn),网上蛋疼的KMP是讲解很多,基本都是走的高大上路线看的你也是一头雾水,我试图用自己的理解用最接地气的方式描述 KMP KMP也是一种优化版的

  • 快速模式匹配算法(KMP)的深入理解

    恐怕现在用过电脑的人,一定都知道大部分带文本编辑功能的软件都有一个快捷键ctrl+f 吧(比如word).这个功能主要来完成"查找","替换"和"全部替换"功能的,其实这就是典型的模式匹配的应用,即在文本文件中查找串.1.模式匹配模式匹配的模型大概是这样的:给定两个字符串变量S和P,其中S成为目标串,其中包含n个字符,P称为模式串,包含m个字符,其中m<=n.从S的给定位置(通常是S的第一个位置)开始搜索模式P.如果找到,则返回模式P在目标

  • Python字符串匹配算法KMP实例

    本文实例讲述了Python字符串匹配算法KMP.分享给大家供大家参考.具体如下: #!/usr/bin/env python #encoding:utf8 def next(pattern): p_len = len(pattern) pos = [-1]*p_len j = -1 for i in range(1, p_len): while j > -1 and pattern[j+1] != pattern[i]: j = pos[j] if pattern[j+1] == pattern

  • KMP算法精解及其Python版的代码示例

    KMP算法是经典的字符串匹配算法,解决从字符串S,查找模式字符串M的问题.算法名称来源于发明者Knuth,Morris,Pratt. 假定从字符串S中查找M,S的长度ls,M的长度lm,且(ls > lm). 朴素的字符串查找方法 从字符串S的第一个字符开始与M进行比较,如果匹配失败.从下一字符开始,重新比较.指导第 (ls - lm) 个字符. 这种方法容易想到并且容易理解,效率不高. 问题在于每次匹配失败后,移动的步伐固定为 1,其实步子可以迈得再大一些. KMP的字符串查找方法 假定在模式

  • C语言kmp算法简单示例和实现原理探究

    以前看过kmp算法,当时接触后总感觉好深奥啊,抱着数据结构的数啃了一中午,最终才大致看懂,后来提起kmp也只剩下"奥,它是做模式匹配的"这点干货.最近有空,翻出来算法导论看看,原来就是这么简单(下不说程序实现,思想很简单). 模式匹配的经典应用:从一个字符串中找到模式字串的位置.如"abcdef"中"cde"出现在原串第三个位置.从基础看起 朴素的模式匹配算法 A:abcdefg  B:cde 首先B从A的第一位开始比较,B++==A++,如果全

随机推荐