KMP算法 2021-02-02

KMP算法

  • 画了一晚上和一上午时间研究了一下KMP算法

  • 该算法主要功能是匹配字符串,由于传统匹配方法主串存在指针回溯问题,针对此进行改进。

  • 先研究子串性质

    • 首先,字串与主串匹配时第一个不同的字符我们叫当前位置,《大话数据结构》举了两个例子,很有启发性。如下图
      在这里插入图片描述

    • 对于本例当前位置为6,上图是传统匹配步骤,但是由于a不同于bcde,但bcde又分别与主串匹配,所以a没有必要再去匹配主串的这些位置,直接跳到第6步就ok,拿主串的j=1来跟当前位置对应的主串位置匹配即可。这是一种情况,再看下例:

    • 对于本例,当前位置是j=6,上图是传统匹配步骤,由于当前位置左侧ab重复出现一次,由于子串中已知ab=ab,ab又与主串匹配,因此不需要再像图4、5那样匹配一遍,加上上例讲的情况,可以直接跳到第6步匹配,ab直接到重复的下一个位置,匹配j=3处的字符即可。(图上好像有点错误,第一幅图错了,应该跟后几幅图一样)

  • 总结起来,就是看当前位置-1长度字串前后缀的相似度(重合度),如何判断此重合度?使用next[]数组。

  • next[j]数组是各个当前位置(1-j)对应的j(例如j=3)的变化,可以说知道了next[]数组,就可以直接跳过中间步骤,使子串的前缀重合上主串的后缀,比较next[j]位置的字串。

  • 理解了这些之后,下面附上KMP算法程序

- ```C
  void get_next(Srtring T,int *next){
      int i=1;//后缀最后一个字符指针
      int j=0;//回退到j=0,说明没有重复字串,特殊情况取1;前缀的最后一个字符指针
      while(i<T[0]){
          if(j==0||T[i]==T[j]){//回退到初始位置,或者字符匹配时,进入。
              i++;//如果当前字符匹配,说明i+1的当前位置,对应有j个重复字串,
              j++;
              next[i]=j;//若i为当前位置,则跳转到j;
          }else{//若不匹配,则回退
              //可以这样理解,对当前字符串的前缀后缀进行匹配,之前一直匹配,直到这个位置不匹配了,说明当前位置为j,根据next[]数组的用法,j应该跳转为next[j]位置再次跟i处字符匹配。即
              j=next[j];   
          }
      }
  }

其实next数组的计算之所以难,就难在那句话j=next[j],但这句话就是在求next数组时用到了next数组的。理解好next数组的原理和思想,这句话自然就看懂了。
程序运行如下图
在这里插入图片描述

  • 其中7、8步发生了回退。

  • 边界条件:j=0.若发生j=next[1]=0;说明了此时前缀的第一个字符都与后缀的最后一个字符不匹配,说明没有重复字串,其他情况,next[j]取1.更新i,更新后缀最后一个字符继续匹配。

  • 如果T[i]=T[j],如果前缀最后一个字符与后缀最后一个字符匹配,好了,next[i+1]=j+1;即可。

  • 求取完next[]数组即可写匹配程序

  • int Index_Kmp (String s,String T,int pos){
        int i=pos;//从主串pos位置开始
        int j=1;//字串指针
        int next[255];//next数组
        get_next(T,next);//获取next数组
        while(i<=S[0]&&j<=T[0]){//若i小于S的长度,j小于T的长度
            if(j==0||S[i]==T[j]){//j如果回退为0,说明第一个字符都跟当前的i无法匹配,跳过。
                i++;//i++
                j++;//j++,指向子串第一个字符
            }else{
                j=next[j];//从当前位置回退
            }
        }
        if(j>T[0])//当j大于T[0]时,说明完全匹配
            return i-T[0];//返回主串中与子串匹配的串的首字符位置
        else
            return 0;//不匹配。
        
    }
    
    
    
  • 其实整个KMP步骤,就是找当前位置(不匹配位置),找到之后根据next数组回退,拿回退的字符再跟当前字符比较,成功则继续往下匹配,重新找下一个当前位置,不成功继续回退,直到回退到边界j=0,跳过此字符。

  • 看算法窍门:首先要把程序翻译成自然语言,通过例子,弄清楚程序的变量表示数目,程序的步骤是什么。我用的例子是主串bacbababadababacambabacaddababacasdsd,子串ababaca,你就对着程序匹配上述字符串,自然就明白程序原理了。

  • 翻译成自然语言之后,就容易理解了,其实没那么复杂,很巧妙。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值