zoukankan      html  css  js  c++  java
  • KMP字符串模式匹配算法(C++实现)

    鉴于原理有点复杂,详细原理可以参考这篇文章http://blog.csdn.net/v_july_v/article/details/7041827

      本文直接从结论入手,应付考试和竞赛足够了。

      设T为目标串("aaabbbaabbabcabcabbaba"),pat为模式串("aabbabc")。

      这是模式串的next数组:

    j(下标) 0 1 2 3 4 5 6
    pat a a b b a b c
    next[j] -1 0 1 0 0 1 0

      KMP算法:

      j=0时,next[j]=-1。表示下一趟匹配比较时,模式串的第-1个字符与目标串上次失配的位置对齐。(其实等同于第0个字符与目标串上次失配的下一个位置对齐),模式串需要移动到posT-next[j]的位置。(posT为T串下标)

      j=1时,next[j]=0。表示下一趟匹配比较时,模式串的第0个字符与目标串上次失配的位置对齐。模式串需要移动到posT-next[j]个位置。

      j=2时,next[j]=1。表示下一趟匹配比较时,模式串的第1个字符与目标串上次失配的位置对齐。模式串需要移动到posT-next[j]个位置。

      以此类推......

      

      那么下面只要求出next数组即可,next数组是如何形成的?

      从下标0开始,一直到lengthP-1为止(lengthP是模式串的长度),每次寻找该下标前面前缀与后缀相同的最大长度(前后缀不包括前面整个字符串,即起始位置和终止位置都相等的那一个字符串,下面有解释)。

      j=0时,字符a前面无字符,故标记-1;

      j=1时,字符a前面有字符a,但由于“前后缀不包括前面整个字符串”的规则,所以并没有相同的前后缀一说,故标记为0。

      j=2时,字符b前面有字符aa,前后缀相同的字符串为a,故标记为前后缀的长度1。

      以此类推......(PS:前后缀的计算都是从左到右的)

      其实这样说是为了方便理解next数组,而next数组的实际形成也是一次KMP算法,它也是一个匹配字符串的过程,用后缀去匹配前缀的过程。

      代码如下:

      

     1 #include<iostream>
     2 #include<string>
     3 using namespace std;
     4 string T;
     5 string pat;
     6 void getNext(int next[],int lengthP){//lengthP为模式串P的长度 
     7     int j=0,k=-1;//j为P串的下标,k用来记录该下标对应的next数组的值 
     8     next[0]=-1;//初始化0下标下的next数组值为-1 
     9     while(j<lengthP){ //对模式串进行扫描 
    10         if(k==-1||pat[j]==pat[k]){//串后缀与前缀没有相等的子串或者此时j下标下的字符与k下的字符相等。 
    11             j++;k++; 
    12             next[j]=k;//设置next数组j下标的值为k 
    13         }else
    14             k=next[k];//缩小子串的范围继续比较 
    15     }
    16 }
    17 
    18 int kmp(int k,int next[]){
    19     int posP=0,posT=k;//posP和posT分别是模式串pat和目标串T的下标,先初始化它们的起始位置 
    20     int lengthP=pat.length();//lengthP是模式串pat长 
    21     int lengthT=T.length();//lengthT是目标串T长 
    22     while(posP<lengthP&&posT<lengthT){//对两串扫描 
    23         if(posP==-1||pat[posP]==T[posT]){//对应字符匹配 
    24             posP++;posT++;
    25         }else
    26             posP=next[posP];//失配时,用next数组值选择下一次匹配的位置 
    27     }
    28     if(posP<lengthP) return -1;
    29     else return posT-lengthP;//匹配成功 
    30 }
    31 
    32 int main(){
    33     T="aaabbbaabbabcabcabbaba";
    34     pat="aabbabc";
    35     int lengthP=pat.length();
    36     int next[lengthP]={0};
    37     getNext(next,lengthP);
    38     int pos=kmp(0,next);
    39     cout<<pos<<endl;
    40     cout<<"next[]:";
    41     for(int i=0;i<lengthP;i++){
    42         cout<<next[i]<<" "; 
    43     } 
    44     return 0;
    45 }

      

      

      

      

      

  • 相关阅读:
    持续交付知易行难,想做成这事你要理解这几个关键点
    运维需要懂产品和运营吗?
    云计算和AI时代,运维应该如何做好转型?
    从谷歌CRE谈起,运维如何培养服务意识?
    谷歌SRE运维模式解读
    如何打造好运维组织架构?
    如何在CMDB中落地应用的概念?
    有了CMDB,为什么还需要应用配置管理?
    Vue视图渲染原理解析,从构建VNode到生成真实节点树
    10个Vue开发技巧助力成为更好的工程师(二)
  • 原文地址:https://www.cnblogs.com/ytz1996/p/6345614.html
Copyright © 2011-2022 走看看