字符串的模式匹配

   字符有一类重要运算:模式匹配。设T和P是两个字符串(T的长度为n,P的长度为m,1<=m<=n),T 为目标,P为模式,在T中查找是否有与P相等的子串。如果有,则给出P在T的匹配位置,这个运算被称为模式匹配,模式匹配的方法主要有两种:

 (1)朴素的模式匹配,也称Brute Force算法

 (2)KMP算法

 (3)BM算法


一、朴素的模式匹配

    算法思想:从目标串的的第一个字符起与模式串的第一个字符比较,若相等,则继续对字符进行后续的比较,否则目标串从第二个字符起与模式串的第一个字符重新比较,直至模式串中的每个字符依次和目标串中的一个连续的字符序列相等为止,此时称为匹配成功,否则匹配失败。最坏的情况是每遍比较都在最后出现不等,即没变最多比较m次,最多比较n-m+1遍,总的比较次数最多为m(n-m+1),因此朴素的模式匹配算法的时间复杂度为O(mn),效率低下。

  for(i=0;i<na;i++)
  {
      k=i;
      j=0;
      while(a[k]==b[j])
      {
          k++;j++;
      }
      if(j==nb)
      break;
  }
  printf("%d\n",k-j);

 


二、KMP算法

    Brute Force算法存在大量的重复运算,为了避免重复运算,引入KMP算法。复杂度O(m+n)

   设P为“ABCABB”,这个串中“AB”是P的前缀,也是串“ABCAB”的后缀。所以如果在匹配的时候直到“ABCAB”都匹配成功,而“ABCABB”匹配失败,所以T肯定有串为“ABCAB”,所以可以直接将j指针指向移动到第k位。k满足: 

                                                              P[0 ~ k-1] == P[j-k ~ j-1]

 

ABCABC(i)DHIJ
ABCABB(j)   

 

 

ABCABC(i)DHIJ
   ABC(j)ABB 

   利用已经部分匹配这个有效信息,保持i指针不回溯(T),通过修改j指针(P),让模式串尽量地移动到有效的位置。

   也就是说我们要计算每一个位置j对应的k,所以用一个数组suffix来保存,suffix[j] = k,表示当T[i] != P[j]时,j指针的下一个位置。
 
   KMP算法的关键是求P的前缀suffix[]。
 
  suffix[j] = max{ k| (k<j) ^ (P[0..k-1]==P[0..j-1]的后缀) } ,即suffix[ j ],表示P[0..j-1]的后缀与P的前缀间的最长匹配子串的长度。求suffix[]实际上就相当于用P匹配P的过程。
    
suffix[0]=-1;  //设置P前缀的边界值
suffix[1]=0;
int k=0;        //前缀函数指针初始化
for(int i=2 ; i<=m ; i++)
{
    while(k>=0 && p[k]!=p[i-1])   //沿前缀函数指针追溯p中与p[i-1]相同的字符
        k=suffix[k];              //位置k,即目标串当前字符与p[i]匹配成功失败时,应与p[k+1]比较
    suffix[i] = ++k;
}

  有了前缀函数suffix[],可将P匹配T的过程写成一重循环,从P[0]出发,依次T[0],T[1],T[2],...,T[n-1]。

  若P[j]和T[i]匹配成功,则下一次匹配P[j+1]和T[i+1].

  若P[j]和T[i]匹配失败,则T[i]不断的匹配P[suffix[j]],P[P[suffix[j]]],...,直到匹配成功(T[i]==P[..P[suffix[j]]])或者匹配失败(P[...p[suffix[j]]]=-1);若匹配成功,则下一次比较P[P[...P[suffix[j]]]+1] 与 T[i+1] ;若匹配失败,则下一次比较P[0]与T[i+1]

 

i=0;
j=0;                   //T和P的匹配指针初始化
while(i<n-1 && j<=m-1)
{ 
    if(j==-1 || t[i]==p[j])    //若沿前缀函数指针匹配T[i]失败,则下一次比较p[0]与T[i+1];
          i++;j++;           //若匹配成功,则下一次比较P[j+1]与T[i+1];  
    else
         j=suffix[j];        //取suffix
}
if(j>m-1)                    //匹配完毕
     return (i-(m-1));
 else 
     return (-1);            //匹配失败

 

  

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值