字符串的模式匹配中的算法

字符串的模式匹配是一個比較經典的問題:假設有一個字符串S,稱其爲主串,而後還有一個字符串T,稱其爲子串。html

如今要作的是,從主串S當中查找子串T的位置,若是存在返回位置值,若是不存在返回-1。另外主串又稱爲目標串,算法

子串稱爲模式串。數組

 

暴力匹配算法優化

這是一個經典的串匹配問題,涉及的算法也比較多,先討論第一種簡單的暴力算法,思路以下spa

將主串S的第pos個字符 與 子串T的第一個字符比較, 若相同,繼續比較子串和主串後面的字符。code

若不相同,那麼從主串S的第(pos + 1)個字符開始繼續向後匹配,直到匹配到主串的(S.len - T.len)的位置爲止htm

匹配成功返回索引值,匹配失敗返回-1,下面是實現代碼blog

#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]!='\0')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]='\0'; 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\n"); else printf("find it:%d\n",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]!='\0')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]='\0'; 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\n",++count); return i-j; } }else{ i=i-j+1; //rollback j=0; } count++; } printf("calc: %d\n",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\n",res); else printf("doesn't match\n"); 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]!='\0')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]='\0'; 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\n",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\n%s\n",str1,str2); if(res>0)printf("find it :%d\n",res); else printf("doesn't match\n"); return OK; }

事實上還有一種算法叫BM算法,這種算法的使用目前是多的,並且也比較容易理解,因爲時間有限,下次在此補充,你們能夠參考下面的鏈接

一般狀況下,模式串的長度n 遠小於 目標串的長度m ,所以KMP的算法時間複雜度爲O(m)

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

 


歡迎各位轉載,但請指明出處Demon先生

相關文章
相關標籤/搜索