1. 程式人生 > >[數據結構復習] 1 - KMP算法及其改進

[數據結構復習] 1 - KMP算法及其改進

匹配 回退 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算法及其改進