MP 算法是一種快速串匹配算法,對 BF 算法的改進很大,主要體如今匹配失敗時,目標指針不用回溯,而是利用已經獲得的「部分匹配」結果,將模式向右「滑動」若干位置後繼續比較,避免了頻繁回溯,廣泛提升了匹配的工做效率,所以又被稱爲不回溯的字符串搜索算法。算法
假設有目標串T(t₀,t₁,t₂,t₃,……,tn-1)和模式串P(p₀,p₁,p₂,p₃,……,pm-1),若使用BF算法進行模式匹配,第一輪比較時,若tk≠pk,則算法結束這輪比較。函數
字符串T和P中第一個不相等的字符位置出如今位置k處,因此兩串前k個字符是相等的,能夠用字符串P(p₀,p₁,p₂,p₃,……,pk-1)代替字符串T'(t₀,t₁,t₂,t₃,……,tk-1),因而原目標串可轉化爲T(p₀,p₁,p₂,p₃,……,pk-1,tk,...,tn-1)。在進行第二次比較以前,算法一樣把字符串P總體向後移動一個字符,此時,T與P的關係:spa
在上面的比較中,首先比較的是 P中的首字符p0 與 T中的第2個字符p1,若與相等,則算法順序比較 P中第2個字符P1 與 T中第3個字符P2,若不相等,則算法將模式串P總體向後移動一個字符,此時T與P之間的關係:指針
算法依照相同的次序,首先對 P中字符p0 與 T中字符p2 進行比較,若相等則順序比較後續字符,若不相等,則把P總體向後移動一個字符。code
從上面的流程描述,都是對模式串的字符做比較,因此MP算法先是計算出模式字符串(串P)中各個字符之間的關係,而後再依據此關係與目標字符串(串T)進行匹配。記錄串P中各個字符之間的關係的函數也被稱爲字符串P的失效函數。blog
失效函數的定義域爲 j∈{0, 1, 2, 3, 4, 5, 6},也就是 0~Len(P)-1,Len(P)爲串P的長度。字符串
失效函數的值域的計算:對於 k∈{x | 0≤x<j},且 k 知足 p0 p1 … pk = pj-k pj-k+1 … pj 的最大正整數。table
對於模式串P「caatcat」的失效函數實例(不能知足條件的k不存在則爲-1):class
j | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
p(j) | c | a | a | t | c | a | t |
f(j) | -1 | -1 | -1 | -1 | 0 | 1 | -1 |
:效率
① 當 j = 0,因爲 0≤k<0,因此知足條件的 k 並不存在,因此 j 取 0,f(0) = -1。
② 當 j = 4,k 的可能取值有 0,1,2,3,因爲 p0 = p4,p0p1 ≠ p3p4,p0p1p2 ≠ p2p3p4 以及 p0p1p2p3 ≠ p1p2p3p4,因此 f(4) = 0。
③ 當 j = 5,k 的可能取值有 0,1,2,3,4,同理 p0 ≠ p5,p0p1 = p4p5,p0p1p2 ≠ p3p4p5 ,p0p1p2p3 ≠ p2p3p4p5 以及 p0p1p2p3p4 ≠ p1p2p3p4p5,因此 f(5) = 1。
獲得字符串P的失效函數後,就能夠應用 MP 算法對它進行匹配。
總結一下,上面所述的是在求字符串前綴後綴的部分匹配值,如例子②: j = 4,字符串的子串「caatc」,它的前綴表達式爲{「c」,「ca」,「caa」,「caat」},後綴表達式爲{「aatc」,「atc」,「tc」,「c」},因此由「caatc」的前綴後綴獲得的部分匹配值爲 「c」,對應的就是上面說的 0。
假設模式串 P = 「caatcat」,目標字符串 T = 「ctcaatcacaatcat」。
在第一輪匹配前,首先把模式字符串P與目標字符串T從各自第一個字符起對齊。
有第一輪結果可知,模式字符串與目標字符串在第2個字符處發生失配。檢測到適配後本輪結束,目標指針不發生回溯,仍指向失配的位置。因爲失配發生在第2個字符處,此時 j = 1。因此模式P在下一輪匹配時的起始地址爲 pf(1-1)+1, 即P0。
在第二輪比較中,因爲模式字符串P在的第1個字符處發生失配,此時 j = 0,因此讓目標的指針前進一位,模式的起始比較地址回到p0。
發現模式字符串P中的第7個字符處發生失配,此時 j = 6。可知模式字符串P在下一輪匹配時的起始比較地址爲pf(6-1)+1,即p2。目標指針一樣不發生回溯,仍指向發生失配的位置。
通過第4輪比較後,匹配成功。經過簡單分析,MP算法的時間複雜度大體爲O(m+n),計算模式串的失效函數O(m),利用失效函數進行匹配O(n),m爲模式串P的長度,n爲目標串的長度。
1 /** 2 * MP算法的失效函數 3 * 4 * @param x 5 * @param m 6 * @param mpNext 7 */ 8 void preMp(char x[], int m, int mpNext[]) { 9 int i, j; 10 i = 0; 11 j = mpNext[0] = -1; 12 while (i < m) { 13 while (j > -1 && x[i] != x[j]) 14 j = mpNext[j]; 15 mpNext[++i] = ++j; 16 } 17 } 18 19 /** 20 * MP算法 21 * @param p 模式串 22 * @param t 目標串 23 */ 24 void mp(String p, String t) { 25 int m = p.length(); 26 int n = t.length(); 27 if (m > n) { 28 System.err.println("Unsuccessful match!"); 29 return; 30 } 31 32 char[] x = p.toCharArray(); 33 char[] y = t.toCharArray(); 34 35 int i = 0; 36 int j = 0; 37 int[] mpNext = new int[m+1]; 38 preMp(x, m, mpNext); 39 40 while (j < n) { 41 while (i > -1 && x[i] != y[j]) 42 i = mpNext[i]; 43 i++; 44 j++; 45 if (i >= m) { 46 System.out.println("Matching index found at: " + (j - i + 1)); 47 i = mpNext[i]; 48 } 49 } 50 }