1. 程式人生 > >【KMP】演算法,未改進C++

【KMP】演算法,未改進C++

首先是部分入門解釋:

1:求next陣列

當我們假設 模式串patten 為 aaabc時,
     a a a b c
對應的 NEXT陣列為:
    -1 0 1 2 0。
Next 陣列的含義:
  求next陣列的時候,對於模式串的 J 位置 ,考察patten[ 0 ].到patten[j-1]組成的字串 最長且相等的字首和字尾。
假設最大相等字首和字尾長度為k,則有k使得  p[0]p[1]p[2]......p[k-2]p[k-1] = p[j-k]p[j-k+1]......p[j-2]p[j-1]。
  先理解 字首 和 字尾,例 "aaabc" 字串的字首,字尾各有4種,相互獨立的。
字首           字尾
a           a a b  c
a a            a b c
a a a            b c
a a a b            c
字首不包含 最後一個字元,
字尾不包含 第一個字元。
patten[0]前無字元,亦無後綴,所以next[0]=-1;表示不存在。
patten[1],在patten[0]~patten[0]找前後綴,均為空,next[1]=0;
patten[2],最長相等字首 'a',字尾為  'a'

,next[2]=1;
patten[3],最長相等前後綴為 ‘aa’和‘aa’ ( 即字首 patten[0]patten[1],字尾 patten[1]patten[2])next[3]=2;
patten[4],字尾中含有 patten[3]=b,字首沒有 ‘b’,next[4]=0;
 求該陣列過程如下:
演算法解釋如下:(關於回溯過程的解釋,說有些囉嗦,可以自己求一下,我的過程在最下面的貼圖上)

2 :KMP解釋

在BF暴力演算法中,假如從文字串的第 i 個字元來開始於模式串匹配。當匹配到模式串的第j位發現失配

即text[i+j] != patten[j]的時候,我們又從文字串的第i+1個位置來重新開始匹配。

可以看到每次失配之後,我們需要從文字串的下一個位置[ i+1 ]匹配,模式串從第一個字元重新開始於文字串匹配。並且在已經知道很多字元都配不上的情況下,還要這樣一個一個字元移動著去匹配,是非常浪費時間的。

假設文字串長為 n,模式串長為 m,BF暴力窮舉方法時間複雜度O(n*m),

而KMP演算法的實質就是,當遇到text[i+j] != patten[j]的時候,但是我們知道模式串中的 0~j-1 位置上的字元已經於i ~ i+j-1位置上的字元是完全匹配的。這樣我們可以在 0 ~ (j-1) 中找到一個字首A和字尾B相等並且最長的那個串,然後將A移動到B的位置再開始重新匹配即可。

這樣就減少了一些不必要的匹配。時間複雜度O(n+m)

借用其他博主的圖片和字串

patten模式串和next陣列
模式串patten和next陣列

準備好next陣列後開始kmp匹配,開始匹配位置  i=0,可以看到,前面的紅色黃色處均匹配,在藍色標(J=5)記處不匹配,藍色處字元對應的next陣列值為 2 ,說明 藍色字元C前面最長相等的前後綴為 a b,長度為2,因為紅色格子表示前後綴相等,所以把模式串划過去,直接讓 黃色格子 a 直接對準 上次藍色格子 C對應的文字串位置

kmp匹配1

模式串滑過去後如圖,這樣就避免了BF演算法中的 i=0匹配失敗,繼續匹配不可能匹配的 i=1,i=2兩個位置,恰好移動後,模式串和文字串匹配,假設模式串和文字串不匹配,模式串就繼續滑動。

kmp_2

 

程式碼如下

/* KMP演算法
   未改進的
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int maxn=1000;    //假設最長字串長度
char text[maxn];        //文字串(目標串 被檢測)
char patten[maxn];      //模式串
int next[maxn];         //模式串的 next陣列
/*
*/
void GetNext(){         //獲取 next陣列
    int Len_p=strlen(patten);
    next[0]=-1;
    int k=-1,j=0;
    while(j<Len_p){
        if(k == -1 || patten[j] == patten[k]){
            ++k; ++j;
            next[j]=k;
        }else{
            k=next[k];
        }
    }
}

int KMP(){                //KMP演算法
    int Addr=-1,i=0,j=0;
    int Len_p=strlen(patten),Len_t=strlen(text);
    while(i<Len_t){
        if(j == -1 || text[i] == patten[j]){
            ++i; ++j;
        }else{
            j=next[j];
        }
        if(j == Len_p){
            return i-Len_p;
            break;
        }
    }
    return Addr;
}
int main(){
    scanf("%s%s",patten,text);
    GetNext();
    for(int i=0;i<strlen(patten);i++){
        printf("%d ",next[i]);
    }   printf("\n");
    printf("%d",KMP());
    return 0;
}

執行如下:

第一行輸入 模式串 asd,第二行輸入文字串 asdf,第一行結果是模式串的next陣列,第二行為模式串第一次出現在文字串的位置

 結果

求模式串“aaabc”的next陣列的演算法每一步

next

引用參考https://blog.csdn.net/suguoliang/article/details/77460455#commentBox

博主:黑脈金