串的模式匹配算法理论与思想

 串的模式匹配算法

一、基本概念

1、模式匹配(定位)

设有主串S和子串T(将S称为目标串,将T称为模式串),在主串S中,从位置start开始查找,如若在主串S中找到一个与子串T相等的子串,则返回T的第一个字符在主串中的位置,否则返回-1

 

2、算法目的

          确定主串中所含子串第一次出现的位置(定位)

 

3、算法种类

     BF算法  (又称古典的、经典的、朴素的、穷举的)

 

          KMP算法

1Brute-Force算法的设计思想:

•         将主串S的第一个字符和模式T的第1个字符比较,

    若相等,继续逐个比较后续字符;

    若不等,从主串S的下一字符起,重新与T第一个字符比较。

•         直到主串S的一个连续子串字符序列与模式T相等。返回值为S中与T匹配的子序列第一个字符的序号,即匹配成功。

  否则,匹配失败,返回值 -1

 

2 Brute-Force算法的实现  

        typedef struct

       {  char str[MaxSize];

           int length;

        }String;

int BFIndex(String S, int start, String T)

   {       int i = start, j = 0, v;   

       while(i < S.length && j < T.length)

       {      if(S.str[i] == T.str[j])   {i++;      j++; }

              else{       i = i-j+1;  j = 0;          }

       }

            if (j==T.length) v=i-T.length;

           else v=-1;

       return v;

    }

3BF算法的时间复杂度

    讨论:

    若n为主串长度,m为子串长度,则串的BF匹配算法最坏的情况下需要比较字符的总次数为(n-m+1)*mO(n*m)

    最好的情况是:一配就中!  只比较了m次。

    最恶劣情况是:主串前面n-m个位置都部分匹配到子串的最后一位,即这n-m位比较了m次,别忘了最后m位也各比较了一次,还要加上m!所以总次数为:(n-m)*m+m (n-m+1)*m

 

 能否利用已部分匹配过的信息而加快模式串的滑动速度?

   能!而且主串S的指针i不必回溯!最坏情况也能达到O(n+m)

 

请看KMP算法!

三、KMP算法
   1、KMP算法设计思想:

   尽量利用已经部分匹配的结果信息,尽量让i不要回溯,加快模式串的滑动速度。

 如图:



 

 

需要讨论两个问题:

①如何由当前部分匹配结果确定模式向右滑动的新比较起点k

模式应该向右滑多远才是高效率的?

 如图:

 
新起点 k怎么求?

 

根据模式串T的规律:   “T0…Tk-1”=“Tj-k …Tj-1”

由当前失配位置j(已知) ,可以归纳计算新起点 k的表达式。

 如图:



  

   (1k值仅取决于模式串本身而与相匹配的主串无关。

   (2k值为模式串从头向后及从j向前的两部分的最大相同子串的长度。

   (3)这里的两部分子串可以有部分重叠的字符,但不可以全部重叠。

         next[j]函数表征着模式T中最大相同前缀子串和后缀子串(真子串)的长度。

    可见,模式中相似部分越多,则next[j]函数越大,它既表示模式T字符之间的相关度越高,也表示j位置以前与主串部分匹配的字符数越多。

    即:next[j]越大,模式串向右滑动得越远,与主串进行比较的次数越少,时间复杂度就越低(时间效率)。

 

    再想一想:如果主串是外存中一个大文件,用KMP算法效果又如何?

         如图:



 

   下一个要讨论的问题是:如何用递推方式来求出最大相同子串的长度呢?这个问题一旦解决,整个KMP算法就可以掌握得很透彻了。

   求子串next[i]值的算法:

  void GetNext(String T, int next[])

  {        int j = 0, k = 0;

           next[0] = -1;

           while(j < T.length){

              if(T.str[j]==T.str[k])

              {   next[j+1]=k+1;  j++;  k++;      }

              else if (k==0){ next[j+1]=0;  j++;  }

         else k=next[k];

            }

   }

KMP算法的思想

s为主串,t为模式串,设i为主串s当前比较字符的下标,j为模式串t当前比较字符的下标,令ij的初值为0。当si = tj时,ij分别增1再继续比较;否则 i不变,j改变为next[j]值(即模式串右滑)后再继续比较。依次类推,直到出现下列两种情况之一:一是 j退回到某个j=next[j]值时有si = tj ,则 ij分别增1后再继续比较;二是j退回到j=-1时,令主串和子串的下标各增1,随后比较si+1t0 。这样的循环过程一直进行到变量大于等于S.length或变量j大于等于T.length时为止。

 

KMP算法的实现

  第一步,先把模式T所有可能的失配点j 所对应的next[j]计算出来;

  第二步:执行定位函数Index_kmp (与BF算法模块非常相似)

int KMPIndex(String S, int start,String T, int next[ ])

{int i=start,j=0,v;

    while ( i<=S.length && j<T.length ) {

      if (j==-1|| S.str[i] = = T.str[j] ) {i++;   j++ }   //不失配则继续比较后续字符

       else j=next[j];  //特点:S第一步,先把模式T所有可能的失配点j 所对应的next[j]

     }

  if(j==T.length) v=I-T.length; 

  else    v=-1;                  

return v;

}

主函数

void main(void)

  {       String S = {{"cddcdc"}, 6}, T = {{"cdc"}, 3};

           int next[8], pos;

           GetNext(T, next);

           pos = KMPIndex(S, 0, T, next);

           printf("pos = %d\n", pos);

   }

2KMP算法的时间复杂度

    回顾BF的最恶劣情况:ST之间存在大量的部分匹配,比较总次数为: (n-m+1)*mO(n*m)

    而此时KMP的情况是:由于指针i无须回溯,比较次数仅为n,即使加上计算next[j]时所用的比较次数m,比较总次数也仅为n+m=O(nm),大大快于BF算法

   注意:由于BF算法在一般情况下的时间复杂度也近似于O(n+m),所以至今仍被广泛采用。

KMP算法的用途:

   因为主串指针i不必回溯,所以从外存输入文件时可以做到边读入边查找——“流水作业

如图:



 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 简单字符模式匹配算法:也称为朴素字符匹配算法,是一种基础的字符匹配算法。它的思想是从主的第一个字符开始,依次比较主和模式中对应位置的字符是否相等,如果相等则继续比较,直到模式中所有字符都匹配成功,或者有一个字符不匹配为止。如果不匹配,则将主的起始位置向后移动一位,重新开始匹配。该算法的时间复杂度为O(m*n),其中m和n分别为主和模式的长度。 2. 首位字符模式匹配算法:也称为BF算法(Brute Force),是一种改进的字符匹配算法。它的思想是在简单字符模式匹配算法的基础上,当发现主中某个字符与模式中的某个字符不匹配时,不是将主的起始位置向后移动一位,而是将模式的起始位置向前移动到上一次比较成功的位置之后的下一位,继续匹配。这样可以减少比较次数,提高匹配效率。该算法的时间复杂度为O(m*n),其中m和n分别为主和模式的长度。 3. KMP字符模式匹配算法:是一种高效的字符匹配算法。它的核心思想是利用模式自身的特性,预处理出一个next数组,使得在匹配过程中,当出现不匹配的情况时,可以通过next数组中的信息,跳过一部分比较,从而提高匹配效率。具体实现方法是,在预处理next数组时,从模式的开头开始,计算出每个位置对应的最长前缀和最长后缀的公共部分长度,保存在next数组中。在匹配过程中,当出现不匹配的情况时,根据next数组中的信息,将模式的起始位置向后移动一定的距离,从而跳过一些比较。该算法的时间复杂度为O(m+n),其中m和n分别为主和模式的长度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值