[數據結構復習] 1 - KMP算法及其改進
阿新 • • 發佈:2019-02-25
匹配 回退 del 前綴 || 技術分享 數據結構 == alt
數據結構復習[1] - KMP算法及其改進
模式匹配
模式匹配就是給定模式串和主串,在主串中找模式串第一次出現的位置的算法。
BF算法
BF算法就是暴力匹配算法,下面給個簡單代碼就過吧。
char* s1 = "abcaba";//主串 char* s2= "aba";//模式串 int i=0;//主串中的位置 int j=0;//模式串中的位置 while(i<strlen(s1)&&j<strlen(s2)){//防止越界 if(s1[i]==s2[j]){//匹配成功則比較下一位 i++; j++; }else{ i=i-j+1;//匹配失敗,i回退 j=0;//j從0開始匹配 } } if(j>strlen(s2)){ cout<<i-j<<endl;//找到了輸出位置 }else{ cout<<-1<<endl;//沒找到輸出-1 }
KMP算法
上面的暴力算法回退的太多,很多無用的比較,浪費時間,KMP算法解決了這個問題,提出了next數組,使得在一次比較失敗之後可以快速跳過無用比較,大大簡化了算法。其思路是主串的i每次不回退,j不一定回退到0,而是按照next數組回退到對應的位置,那麽關鍵就是next數組的計算了。
next數組
next數組存在的意義是為了簡化j的回退,方便理解而言,提出了最長公共前後綴的概念,但實際代碼中並不體現這種理解。
例如,對於字符串“abcsab”的前綴串集合為{a,ab,abc,abcs,abcsa},後綴串集合為{b,ab,sab,csab,bcsab},可以看出最大公共前後綴字串為ab。長度為2,因此對應next數組值就是2。
求解代碼:
void get_next(char* s,int* next){ next[0] = -1; next[1] = 0; int k = 0;//初始位置next值 int i = 2;//初始需要賦值的位置 while(i<strlen(s)){ if(k==-1||s[i-1]==s[k]){//如果相等,next的值就是當前位置next的值+1 next[i++] = ++k; }else{ k = next[k];//不相等則繼續往前找,直到k=-1或者找到相等的 } } }
模式匹配代碼:
int KMP(char* m,char* s,int pos){//pos是指從pos位置開始匹配
int i=pos;
int j=0;
int* next = new int[strlen(s)];
get_next(s,next);
while(i<strlen(m) && j<strlen(s)){
if(j==-1 || m[i]==s[j]){
j++;
i++;
}else{
j = next[j];//直接按照next數組進行回退
}
}
delete next;
if(j>=strlen(s)){
return i-j;
}else{
return -1;
}
}
改進的KMP算法
上面的KMP算法存在的問題是,如果模式串為"aaaaaaaab",那麽其next數組的值便是[-1,0,1,2,3,4,5,6,7],也就意味著,如果最後一個a不匹配,那麽將一步一步回退j,回退了之後進行重復的比較,繼續回退(因為還是a,還是失配)。所以我們如果能判斷s[i]是否與s[k]相等,如果相等,其next值直接為next[k]就好了。所以改進後的next數組值應該為[-1,-1,-1,-1,-1,-1,-1,-1,0]
改進的next求解代碼
int* get_next_plus(char* s,int* next){
next[0] = -1;
int i=0;
int k=-1;
while(i<strlen(s)){
if(k==-1 || s[i]==s[k]){
i++;
k++;
if(i>=strlen(s)){
break;
}
if(s[i]==s[k]){
next[i] = next[k];
}else{
next[i] = k;
}
}else{
k = next[k];
}
}
}
[數據結構復習] 1 - KMP算法及其改進