zoukankan      html  css  js  c++  java
  • 字符串的模式匹配中的算法

    字符串的模式匹配是一个比较经典的问题:假设有一个字符串S,称其为主串,然后还有一个字符串T,称其为子串。

    现在要做的是,从主串S当中查找子串T的位置,如果存在返回位置值,如果不存在返回-1。另外主串又称为目标串,

    子串称为模式串。

    暴力匹配算法

    这是一个经典的串匹配问题,涉及的算法也比较多,先讨论第一种简单的暴力算法,思路如下

    将主串S的第pos个字符 与 子串T的第一个字符比较, 若相同,继续比较子串和主串后面的字符。

    若不相同,那么从主串S的第(pos + 1)个字符开始继续向后匹配,直到匹配到主串的(S.len - T.len)的位置为止

    匹配成功返回索引值,匹配失败返回-1,下面是实现代码

    #include <stdio.h>
    #include <stdlib.h>
    
    #define OK 1
    
    typedef int Status;
    typedef struct {
      char *data;
      int  len;
    }String;
    
    Status initString(String *T){
      T->data = NULL;
      T->len  = 0;
      return OK;
    }
    
    Status strAssign(String *T,char *str){
      if(T->data)free(T->data);
    
      int i=0,j;
      while(str[i]!='')i++;
      if(!i){T->data=NULL;T->len=0;}
      else{
        T->data=(char*)malloc(i*sizeof(char));
        for(j=0;j<i;j++){
          T->data[j]=str[j];
        }
        T->data[j]='';
        T->len=i;
      }
      return OK;
    }
    
    int Index_SimpleMode(String T,String S,int pos){
      if( S.len == 0 || T.len<S.len )return -1;
    
      int i=pos, j=0;
      while(i<T.len && j<S.len){
        if(T.data[i] == S.data[j]){i++,j++;}
        else{i=i-j+1;j=0;}
      }
      if(j==S.len)return i-S.len;
      else return -1;
    }
    
    int main(){
      int pos,idx;
      String S1,S2;
      initString(&S1);
      initString(&S2);
    
      char *main = (char*)malloc(100*sizeof(char));
      char *sub  = (char*)malloc(100*sizeof(char));
    
    
      printf("enter string:");
      scanf("%s",main);
    
      printf("enter string:");
      scanf("%s",sub);
    
      printf("enter index:");
      scanf("%d",&idx);
    
      strAssign(&S1,main);
      strAssign(&S2,sub);
    
      pos = Index_SimpleMode(S1,S2,idx);
      if(pos<0)printf("doesn't match
    ");
      else printf("find it:%d
    ",pos);
    
      return OK;
    }

    设主串S的长度是n  子串T的长度是m

    最好的情况是:主串为aaaaaabc, 子串为bc,此时执行的次数是 ( m + n ) / 2 

    时间复杂度为O(n+m)

    最坏的情况是:主串为000000001,子串为01,此时执行的次数是 m * ( n - m + 2 ) / 2

    时间复杂度为O(m*n)

    双向匹配算法

    可以看到上面所示最坏的情况需要不断回滚,可以限制匹配成功的条件,也就是模式串的首尾同时匹配上

    那么再继续进行匹配,这个算法我称其为双向匹配算法,先不管该算法有没有很牛的名字,其思路是在暴力

    匹配的基础上加上 模式串的尾部也需要相同才算匹配成功,然后首尾两头向中间移动继续匹配字符,因此如果

    模式串的一半长度匹配成功,那么另一半也就匹配成功,则返回成功匹配的索引值,否则返回-1

    #include <stdio.h>
    #include <stdlib.h>
    
    #define OK 1
    
    typedef int Status;
    typedef struct {
      char *data;
      int  len;
    }String;
    
    Status initString(String *T){
      T->data = NULL;
      T->len  = 0;
      return OK;
    }
    
    Status strAssign(String *T,char *str){
      if(T->data)free(T->data);
    
      int i=0,j;
      while(str[i]!='')i++;
      if(!i){T->data=NULL;T->len=0;}
      else{
        T->data=(char*)malloc(i*sizeof(char));
        for(j=0;j<i;j++){
          T->data[j]=str[j];
        }
        T->data[j]='';
        T->len=i;
      }
      return OK;
    }
    
    int twoWayMode(String T,String S){
      if(S.len > T.len)return -1;
      int i=0, j=0, m=S.len-1, n=m/2;
      int count = 0;
      while(i<T.len && j<S.len){
    // i-j          目标串的头部位置
    // (i-j)+ m 目标串的尾部位置
    // (i-j)+ (m-j)    目标串与模式串成功匹配j位后的尾部索引
    if(T.data[i]==S.data[j] && T.data[i+m-2j]==S.data[m-j]){ ++i; ++j; if(j>n){ //improve; printf("count is %d ",++count); return i-j; } }else{ i=i-j+1; //rollback j=0; } count++; } printf("calc: %d ",count); return -1; } int main(){ char str1[] = "ABCABd ABdsadA ABCAdsaABddsadasdaABCDsadCaDdsaABCDEFGH"; char str2[] = "ABCDEFGH"; String S1,S2; initString(&S1); initString(&S2); strAssign(&S1,str1); strAssign(&S2,str2); int res = twoWayMode(S1,S2); if(res>=0)printf("find it: %d ",res); else printf("doesn't match "); return OK; }

    其时间复杂度与暴力匹配的时间复杂度基本相同,但是新的算法的复杂度更接近于最好的情况也就是O(m + n)

    KMP匹配算法

    还有一种改进的算法是KMP算法,这个算法不太好理解

    因此这里找了一篇看过中讲的最好的文章:

    http://wiki.jikexueyuan.com/project/kmp-algorithm/define.html

    当然可以先看阮一峰的文章,方便理解:

    http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html

    #include <stdio.h>
    #include <stdlib.h>
    
    #define OK 1
    
    typedef int Status;
    typedef struct {
      char *data;
      int  len;
    }String;
    
    Status initString(String *T){
      T->data = NULL;
      T->len  = 0;
      return OK;
    }
    
    Status strAssign(String *T,char *str){
      if(T->data)free(T->data);
    
      int i=0,j;
      while(str[i]!='')i++;
      if(!i){T->data=NULL;T->len=0;}
      else{
        T->data=(char*)malloc(i*sizeof(char));
        for(j=0;j<i;j++){
          T->data[j]=str[j];
        }
        T->data[j]='';
        T->len=i;
      }
      return OK;
    }
    
    //next数组算法
    void getNext(String S,int *next){ next[0] = -1; int k=-1, j=0; while(j<S.len-1){ if(k==-1 || S.data[j]==S.data[k]){ ++k; ++j; next[j]=k; }else{ k=next[k]; } } int i; }
    //优化后的next数组算法
    void newNext(String S,int *next){ next[0] = -1; int k=-1, j=0; while(j<S.len-1){ if(k==-1 || S.data[j]==S.data[k]){ ++k; ++j; if(S.data[j]!=S.data[k]) next[j] = k; else next[j] = next[k]; } else{ k=next[k]; } } } int KMPSearch(String T,String S){ int i=0, j=0, next[S.len], c=0; //getNext(S,next); newNext(S,next); while(i<T.len && j<S.len){ if(j==-1 || T.data[i]==S.data[j]){i++;j++;} else{ j=next[j]; } c++; } printf("[TEST]:newNext: %d ",c); if(j==S.len)return i-j; else return -1; } int main(){ char str1[] = "ABCABd ABdsadA ABCAdsaABddsadasdaABCDsadCaDdsaABCDEFGH"; char str2[] = "ABCDEFGH"; String S1,S2; initString(&S1); initString(&S2); strAssign(&S1,str1); strAssign(&S2,str2); int res = KMPSearch(S1,S2); printf("%s %s ",str1,str2); if(res>0)printf("find it :%d ",res); else printf("doesn't match "); return OK; }

    事实上还有一种算法叫BM算法,这种算法的使用目前是多的,而且也比较容易理解,由于时间有限,下次在此补充,大家可以参考下面的连接

    通常情况下,模式串的长度n 远小于 目标串的长度m ,因此KMP的算法时间复杂度为O(m)

    http://wiki.jikexueyuan.com/project/kmp-algorithm/bm.html

     


    欢迎各位转载,但请指明出处Demon先生








  • 相关阅读:
    hdoj5327【前缀和思想】
    codeforces 611C
    codeforces 632C
    poj1163 【记忆化搜索·水】
    2016 Multi-University Training Contest 2 A Acperience
    细剖最短路【闲谈,权当是饭后杂文】
    poj1724【最短路】
    [YY题]HDOJ5288 OO’s Sequence
    Codeforces Round #363 (Div. 2) C. Vacations
    POJ2105【进制转化】
  • 原文地址:https://www.cnblogs.com/demonxian3/p/7471408.html
Copyright © 2011-2022 走看看