字符串與模式匹配算法(二):MP算法

1、MP算法介紹

  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中的首字符p與 T中的第2個字符p1,若與相等,則算法順序比較 P中第2個字符P與 T中第3個字符P2,若不相等,則算法將模式串P總體向後移動一個字符,此時T與P之間的關係:指針

  算法依照相同的次序,首先對 P中字符p與 T中字符p進行比較,若相等則順序比較後續字符,若不相等,則把P總體向後移動一個字符。code

  從上面的流程描述,都是對模式串的字符做比較,因此MP算法先是計算出模式字符串(串P)中各個字符之間的關係,而後再依據此關係與目標字符串(串T)進行匹配。記錄串P中各個字符之間的關係的函數也被稱爲字符串P的失效函數blog

2、MP算法中模式串的失效函數

  失效函數的定義域爲 j∈{0, 1, 2, 3, 4, 5, 6},也就是 0~Len(P)-1,Len(P)爲串P的長度。字符串

  失效函數的值域的計算:對於 k∈{x | 0≤x<j},且 k 知足 pp1 … p= pj-k pj-k+1 … p的最大正整數。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 ≠ p2p3p以及 p0p1p2p3 ≠ p1p2p3p4,因此 f(4) = 0。

  ③ 當 j = 5,k 的可能取值有 0,1,2,3,4,同理 p0 ≠ p5,p0p1 = p4p5,p0p1p2 ≠ p3p4p,p0p1p2p3 ≠ p2p3p4p5 以及 p0p1p2p3p4 ≠ p1p2p3p4p5,因此 f(5) = 1。

  獲得字符串P的失效函數後,就能夠應用 MP 算法對它進行匹配。

  總結一下,上面所述的是在求字符串前綴後綴的部分匹配值如例子②: j = 4,字符串的子串「caatc」,它的前綴表達式爲{「c」,「ca」,「caa」,「caat」},後綴表達式爲{「aatc」,「atc」,「tc」,「c」},因此由「caatc」的前綴後綴獲得的部分匹配值爲 「c」,對應的就是上面說的 0。

3、MP函數使用失效函數對字符串進行匹配

  假設模式串 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爲目標串的長度。

4、代碼

 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     }
相關文章
相關標籤/搜索