KMP演算法詳解

2022-05-27 15:15:10 字數 1622 閱讀 8567

kmp演算法, 又稱模式匹配演算法,能快速判斷字串b是否為字串a的子串。設a的長度為n,b的長度為n,則kmp演算法的時間複雜度為o(n+m)。

在講解kmp演算法之前,先將一種易懂的解決這類問題的方法:列舉a的每個元素$a_i$,每次列舉時比較$a_i$與$b_1,a_$與$b_2$,...,$a_$與$b_n$是否相等,若全部相等,則b為a的子串。時間複雜度o(nm);

顯然這個方法太慢了,因此我們需要kmp演算法來更高效地解決這類問題。當然,用hash也可以解決這類問題,不過用kmp演算法會更優一些。

若字串b為a的子串,則顯然a中存在至少存在一段字元與b的所有字首相匹配;若這段字元的長度等於n,則b為a的子串。因此我們定義乙個f陣列,$f_i$表示a中以i結尾子串與b的字首匹配的最長長度。

如何進行匹配呢?若當前以i結尾的長度為j的a的子串與b的長度為i的字首匹配,則繼續比較$a_$與$b_$是否相等,若相等則擴充套件子串長度,若不相等則需要縮小j,繼續進行匹配。

如何縮小j呢?若乙個乙個地縮小j,顯然效率太低。我們可以發現,當a[i-j~i]與b[1~j]匹配時,若有b[1~k]與b[i-k~i]匹配,且有b[k-l~k]與b[1~l]匹配,則有b[i-l~l]與b[1~l]匹配。因為若b[1~k]與b[i-k~i]匹配,說明k之前包含k的l個字元和i之前包含i的l個字元是相同的,也就是b[k-l~k]與b[i-l~i]匹配,所以有b[i-l~l]與b[1~l]匹配。為了使列舉的長度盡量地長,因此我們需要找到乙個最大的符合條件的l。這個用和上一段的匹配非常類似的遞推就可以實現了。

首先,我們定義乙個陣列next,$next_i$表示b中以i結尾的非字首子串與b的字首匹配的最長長度。若當前以i為結尾的長度為j的b的非字首子串與b的長度為i的字首匹配,則繼續比較$b_$與$b_$是否相等,若相等則擴充套件子串長度,若不相等則縮小j,繼續進行匹配。

在這裡又如何縮小j呢?因為我們遞推時是按照下標公升序進行的,因此next[1~j-1]都已求出,所以我們直接取j=next[j]就可以了。如果不斷地縮小j都無法匹配,則從頭開始。

遞推next陣列**:

void pre()

}

遞推出next陣列後,進行匹配遞推f陣列就非常簡單了。因為兩個遞推思想類似,因此**也十分相似。

遞推f陣列**:

void calm()

}

總體實現過程如下圖所示:

本文的**均使用string型別儲存字串,而string型別的字串下標是從0開始的,但題目中的位置大多從1開始,因此在很多地方需要進行特殊處理。這些處理會在下面的**中一一說明。

next陣列和f陣列的定義大小:通過上面的講解應該很明顯了,定義next[m],f[n]。在**實現中,為了防止出鍋,應該把陣列定義得略大一些。

完整**:

#include#includeusing namespace std;

const int n=2e6;

int next[n],f[n];

string a,b;

void pre()

}void calm()

}int main()

{ cin>>a>>b;

pre();

calm();

for(int i=0;i

習題:2019.4.7 於福建省石獅市

KMP演算法詳解

模式匹配的kmp演算法詳解 這種由d.e.knuth,j.h.morris和v.r.pratt同時發現的改進的模式匹配演算法簡稱為kmp演算法。大概學過資訊學的都知道,是個比較難理解的演算法,今天特把它搞個徹徹底底明明白白。注意到這是乙個改進的演算法,所以有必要把原來的模式匹配演算法拿出來,其實理解...

KMP演算法詳解

kmp演算法即knuth morris pratt演算法,是模式匹配的一種改進演算法,因為是名字中三人同時發現的,所以稱為kmp演算法。因為偶然接觸到有關kmp的問題,所以上網查了一下next陣列和 nextval陣列的求法,卻沒有找到,只有在csdn的資料檔案裡找到了next陣列的簡單求法 根據書...

KMP演算法詳解

相信很多人 包括自己 初識kmp演算法的時候始終是丈二和尚摸不著頭腦,要麼完全不知所云,要麼看不懂書上的解釋,要麼自己覺得好像心裡了解kmp演算法的意思,卻說不出個究竟,所謂知其然不知其所以然是也。經過七八個小時地仔細研究,終於感覺自己能說出其所以然了,又覺得資料結構書上寫得過於簡潔,不易於初學者接...