数据结构-字符串(BF/KMP算法)

一、查找字符串字串

  1. BF算法
    暴力匹配(BF)算法是普通的模式匹配算法,BF算法的思想就是将目标串S的第一个字符与模式串T的第一个字符进行匹配,若相等,则继续比较S的第二个字符和T的第二个字符;若不相等,则比较S的第二个字符和T的第一个字符,依次比较,直到得出最后的匹配结果。

     int BF(const char *str,const char *sub,int pos)
     {
         assert(str!=NULL&&sub!=NULL);
         int i=pos;//用i来记录S中字符的位置
         int j=0;//用j来记录T中字符的位置
         int lens=strlen(str);
         int lensub=strlen(sub);
         //当S不为空并且T不为空时,逐个进行字符的比较
         while(j<lensub&&i<lens)
         {
             if(str[i]==sub[i])
             //当S中第i个字符与T中第j个字符相等时,i向后移一个,j向后移一个
             {
                 i++;
                 j++;
             }
             else 
             //当S中第i个字符与T中第i个字符不相等时,i回到前一个位置的下一个位置,j回到0号位置
             {
                 i=i-j+1;
                 j=0;
             }
     
         }
         if(j>=lensub)
         //当j走完T的长度时,也就说明T在S中匹配成功
         {
             return i-j;
             //此时返回字串在主串中的下标位置,此时i在该返回下标位置+j的长度位置,所以返回i-j,本例中返回下标3
         }
         //匹配失败,则返回-1,因为-1下标不存在,不能返回0下标
         else return -1;
     }
    

    关于BF算法:

    1. 当第一个字符不相同时j也会继续向后比较,比如例子中的“abcdefg”和“def”,当“a”和“d”不相同时,则明显之后的两个字符及时相等也不是相同的子串。
    2. 每次j下标都要回到0号下标,当主串和字串匹配失败时,主串进行回溯会影响效率,回溯之后,主串与字串有些部分比较是没有必要的
      综上:
      这种简单的丢弃前面的匹配信息的算法,造成了极大的浪费和底下的匹配效率
  2. KMP算法

    在KMP算法中,对于每一个模式串都会事先计算出模式串的内部匹配信息,在匹配失败时最大的移动模式串,以减少匹配次数,这样就很好的解决了BF算法的缺陷
    比如,当匹配失败后,最好是能够将模式字串尽量的右移和主串进行匹配,右移的距离在KMP算法中是这样计算的:在已经匹配的字串中,找到最长的相同的前缀和后缀,然后移动使他们重叠,这个位置就是j要回退的位置,这样j就不用每一次都回到0号位置了,每一次j回退的位置存储在一个数组里,称之为next数组
    那么j回退的位置什么是最合适的呢
    我们通过一组图来看一下:
    在这里插入图片描述

    根据以上理论,我们求得模式串的next数组:
    在这里插入图片描述
    代码实现:

     #include "stdio.h"
     
     /***********************
     T:  9 a b a b a a a b a
     j:  0 1 2 3 4 5 6 7 8 9
     k:    0 1 1 2 3 4 2 2 3
      *************************/
     
     typedef char *String;
     
     // i: 表示后缀 j: 前缀     前缀是固定的,后缀是相对的
     void GetNext(String T, int *next)
     {
         int i = 1, j = 0;
         while (i < T[0])
         {
             if (j == 0 || T[i] == T[j])
             {
                 i++; //  2 3 
                 j++; //  1 0
                 if (T[i] != T[j])
                 {
                     next[i] = j; //1 1 
                 }
                 else
                 {
                     next[i] = next[j];
                 }
             }
             else
             {
                 j = next[j];  // 0 
             }
         }
     }
     
     int IndexKMP(String s, String T, int pos)
     {
         int i = pos;
         int j = 1;
         int next[255];
         GetNext(T, next);
         while (i <= s[0] && j <= T[0])
         {
             if (s[i] == T[j])
             {
                 i++;
                 j++;
             }
             else
             {
                 j = next[j];
             }
         }
         if (j > T[0])
         {
             return i-T[0];
         }
         else
         {
             return 0;
         }
     }
     
     int main()
     {
         char str[255] = " abababaaaba";
         char t[4] = " aba";
         t[0] = 3;
         int next[255];
         int i = 1;
         str[0] = 11;
         GetNext(str, next);
         for ( i = 1; i < 10; i++)
         {
             printf("%d ", next[i]);
         }
         printf("\n");
         printf("%d", IndexKMP(str, t, 2)) ;
         return 0;
     }
    

二、问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值