a - 数据结构实验之串一:kmp简单应用_串的两种模式匹配方式(BF/KMP算法)
串的兩種模式匹配方式(BF/KMP算法)
前言
串,又稱作字符串,它是由0個或者多個字符所組成的有限序列,串同樣可以采用順序存儲和鏈式存儲兩種方式進行存儲,在主串中查找定位子串問題(模式匹配)是串中最重要的操作之一,而不同的算法實現有著不同的效率,我們今天就來對比學習串的兩種模式匹配方式:
- 樸素的模式匹配算法(Brute-Force算法,簡稱BF算法)
- KMP模式匹配算法
樸素的模式匹配算法(BF算法)
BF算法是模式匹配中的一種常規算法,它的思想就是:
- 第一輪:子串中的第一個字符與主串中的第一個字符進行比較
- 若相等,則繼續比較主串與子串的第二個字符
- 若不相等,進行第二輪比較
- 第二輪:子串中的第一個字符與主串中第二個字符進行比較......
- 第N輪:依次比較下去,直到全部匹配
圖示說明:
第一輪:
第二輪:
...... 原理一致,省略中間步驟
第五輪:
第六輪:
代碼實現:
看完文字與圖例講解,我們來動手實現一個這樣的算法
簡單歸納上面的步驟就是:
主串的每一個字符與子串的開頭進行匹配,匹配成功則比較子串與主串的下一位是否匹配,匹配失敗則比較子串與主串的下一位,很顯然,我們可以使用兩個指針來分別指向主串和子串的某個字符,來實現這樣一種算法
匹配成功,返回子串在主串中第一次出現的位置,匹配失敗返回 -1,子串是空串返回 0
int注:代碼只為體現算法思路,具體定義未給出
這種算法簡單易懂,卻存在著一個很大的缺點,那就是需要多次回溯,效率低下,若主串為 000000000001 子串為00001,這就意味著每一輪都要比較到子串的最后一個字符才會匹配失敗,有沒有更好的辦法呢?下面的KMP模式匹配算法就很好的解決了這一問題
KMP模式匹配算法
如果僅僅進行一些少量數據的運算,可能你甚至覺得BF算法也還行,起碼是很容易寫出來的,畢竟能跑的就是好程序,但是一旦數據量增大,你就會發現有一些 “無用功” 真的會大大的拖慢你的速度
KMP模式配算法是由 D.E.Knuth,J.H.Morris,V.R.Pratt 三位前輩提出的,它是一種對樸素模式匹配算法的改進,核心就是利用匹配失敗后的信息,盡量減少子主串的匹配次數,其體現就是 主串指針一直往后移動,子串指針回溯
圖示說明:
下面所表示的是樸素模式匹配算法的過程,我們看看如果使用KMP算法的思想,哪些步驟是可以省略掉的
① 中前五個元素,均互相匹配,知道第六個元素才匹配失敗,按照BF算法來說,就直接進行 ② ③ 操作,但是,我們可以發現,子串中的前三個元素 a b c 均不是相同的,但是在 ① 中已經與 主串相匹配,所以 子串分別與主串中的第二 第三個元素匹配 一定是不匹配的,所以圖中的 ② ③ 均可以省略
在 ① 中 子串中的 第一第二個元素 ab 和第四第五個元素 ab 是相同的,且 第四第五個元素 ab 已經與主串中的 第四第五個元素匹配成功,這意味著,子串中第一第二個元素 ab 一定與 主串中 第四第五個元素相匹配,所以 ④ ⑤ 步驟可以省略
如果按照這種思路,上面的例子只需要執行 ① 和 ⑥ 就可以了
next 數組值推導
(一) 主串指針是否需要回溯
我們觀察上面的兩種過程 ,BF算法-①②③④⑤⑥,KMP算法-①⑥,如果我們現在假定有兩個指針,i 和 j,分別指向主串和子串中的所處位置,從上圖我們可以知道,主串指針,也就是 i 的值在 ① 的狀態下, 指針指向6的位置,而在 ②③④⑤ 中卻分別指向了2345,而在 ⑥ 中仍指向6的位置
這說明,樸素模式匹配算法,主串的 i 值會不斷的進行回溯,但是 KMP模式匹配算法將這種沒必要的回溯省略掉了,所以減少了執行次數
(二) 子串指針優化總結
既然主串指針不進行回溯,那么還可以優化的就是 子串指針了,一般會遇到兩種情況 我們舉兩個例子:
- 如果子串為 abcdef,主串為abcdexabcdef,當第一輪匹配到第六個字符f和x的時候,匹配失敗了,這個時候如果按照樸素模式匹配,就需要拿子串的首元素a去分別和主串的bcde進行比較,但是由于子串f元素前的元素中沒有相同的元素,并且與主串匹配,所以a與主串中的2-5號元素 即 bcde 都是不可能相匹配的,所有這幾部都可以省略,直接讓a和主串中的x去匹配
- 如果子串為abcabx,主串為abcababcax,在第一輪中,前五個元素子主串分別相匹配,第六個元素位置出錯,按照樸素模式匹配,我們需要拿子串首元素a,依次與主串中的a后面的元素匹配,但是子串前面三個字符abc是不相等的,按照我們第一種情況的經驗,就直接跳過這些步驟了,所有我們直接拿 子串a與 主串第四個元素a進行比較就可以了,但是我們發現,子串中出錯的位置x前的串 abcab 的前綴和后綴都是 ab,既然第一輪的時候,已經匹配成功,那就意味著,子串中的 第一第二個元素ab一定與 主串中 第四第五個元素 ab相等,所以這個步驟也可以省略,也就直接可以拿子串前綴ab后面的c開始于a進行比對,這也就是我們上面圖中例子的詳細思路
總結:所以我們得出規律,子串指針的值取決于,子串前后綴元素的相似程度
想要應用到具體代碼中,我們可以把子串位置變化 的 j 值定義成一個next數組,且長度與子串長度相同
且當當集合不為空時其他情況?
- 情況1:當 j = 0 時,next[j] = -1, 表示子串指針指向下標為0的元素的時候匹配失敗,子串無法回溯,(j不能賦值-1) ,此時將主串指針后移一位,子串不,進行下一輪比較
- 情況2:在已經匹配的子串中,存在相同的前綴串 T0 T1 ... Tk-1 和后綴串 Tj-k Tj-k+1 ... Tj-1,子串指針則回溯到next[j] = k的位置,然后進行下一趟比較,例如:子串 abcabc 有相同前綴和后綴ab 所以子串指針回溯到 c的位置
- 情況3:在已經匹配的子串,若不存在相等的前綴和后綴,則主串指針不動,子串指針回溯到 j = 0 的位置,然后進行下一趟比較
例:主串 S = “abc520abc520abcd”, 子串 T = "abc520abcd" ,利用 KMP算法匹配過程
子串 next 數組
j0 1 2 3 4 5 6 7 8 9子串a b c 5 2 0 a b c dnext[j]-1 0 0 0 0 0 0 1 2 3
可以看到,在 指針 i = 9 且 j = 9 的時候,匹配失敗, 此時 next[9] = 3 ,所以子串指針回溯到 下標 j = 3 的位置也就是元素 5 的位置,進行第二輪比較,然后正好全部匹配成功
(三) 求next數組算法實現
voidKMP算法代碼實現
有了 next 數組的鋪墊,我們就可以來實現KMP算法了
匹配成功返回子串在主串中第一次出現的位置,失敗返回-1,子串為空串返回0
intKMP模式匹配算法改進
有一種特殊情況的出現,使得我們不得不考慮KMP算法的改進
那就是子串中有多個連續重復的元素,例如主串 S=“aaabcde” 子串T=“aaaaax” 在主串指針不動,移動子串指針比較這些值,其實有很多無用功,因為子串中前5個元素都是相同的a,所以我們可以省略掉這些重復的步驟
void這種改進的核心就在于 增加了對子串中 t[i] 和 t[j] 是否相等的判斷,相等則直接將 nextVal[j] 的值賦給 nextVal[i]
總結
在BF算法中,當主串和子串不匹配的時候,主串和子串你的指針都需要回溯,所以導致了該算法時間復雜度比較高為 O(nm) ,空間復雜度為 O(1) 注:雖然其時間復雜度為 O(nm) 但是在一般應用下執行,其執行時間近似 O(n+m) 所以仍被使用
KMP算法,利用子串的結構相似性,設計next數組,在此之上達到了主串不回溯的效果,大大減少了比較次數,但是相對應的卻犧牲了存儲空間,KMP算法 時間復雜度為 O(n+m) 空間復雜度為 O(n)
結尾:
如果文章中有什么不足,或者錯誤的地方,歡迎大家留言分享想法,感謝朋友們的支持!
如果能幫到你的話,那就來關注我吧!如果您更喜歡微信文章的閱讀方式,可以關注我的公眾號
在這里的我們素不相識,卻都在為了自己的夢而努力 ?一個堅持推送原創開發技術文章的公眾號:理想二旬不止
總結
以上是生活随笔為你收集整理的a - 数据结构实验之串一:kmp简单应用_串的两种模式匹配方式(BF/KMP算法)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python turtle渐变色_如何在
- 下一篇: python内置的读取文件函数_pyth