字符串與模式匹配算法(五):BMH算法


一、BMH算法介紹

  在BM算法的實際應用中,壞字符偏移函數的應用次數要遠遠超過好后綴偏移函數的應用次數,壞字符偏移函數在匹配過程中起着移動指針的主導作用。在實際匹配過程,只是用壞字符偏移函數也非常有效。1980年,奈傑爾·豪斯普(Nigel Horspool)提出了改進的BM算法,也就是BMH算法。簡化了BM算法,執行非常方便,效率也很可觀。Boyer-Moore算法使用兩種策略來確定不匹配模式的位移:壞字符策略和高端策略。 來自Horspool的想法是僅使用壞字符策略,而不使用導致不匹配的字符,而始終使用文本窗口的匹配的字符。

二、主要思想

  Horspool建議僅使用窗口最右邊字符的壞字符移位來計算Boyer-Moore算法中的移位。例如:

  (a) Boyer-Moore

0 1 2 3 4 5 6 7 8 9 ...
a b c a b d a a c b a
b c a a b            
  b c a a b          

  (b) Horspool

0 1 2 3 4 5 6 7 8 9 ...
a b c a b d a a c b a
b c a a b            
        b c a a b    

  觀察是上面兩個不同算法的例子,后綴ab匹配,比較c-a表示不匹配。 Boyer-Moore算法(a)根據最后一次出現c的壞字符位置的策略確定滑動距離。 Horspool算法(b)根據最后一次出現的b來確定滑動距離,其中在模式的最后位置出現的b不計算在內。

  同樣在Horspool算法中,最有利的情況是,如果每次第一次比較都發現一個文本字符,而該字符根本不在模式中出現。 然后,該算法僅需要O(n / m)個比較。

  壞字符策略所需的出現函數occ與Boyer-Moore算法中的計算略有不同。 對於每個字母字符a,occ(p,a)是它在p0 ... pm-2中最后一次出現的位置;如果根本不出現該字符,則為-1。 因此,不會考慮該模式的最后一個字符pm-1

  • occ(text, x) = 2

  • occ(textet, t) = 3

  • occ(text, t) = 0

  • occ(next, t) = -1

  這里的occ(textet,t)= 3,因為單詞texte中t的最后一次出現在位置3。 此外,由於單詞tex中t的最后一次出現在位置0,所以occ(text,t)= 0,最后,因為t根本不在nex中出現,所以occ(next,t)= -1。

  給定模式p的出現函數存儲在數組occ中,該數組由字母字符索引。 對於每個字符,元素a,occ [a]包含對應的函數值occ(p,a)。

三、BMH算法代碼

Horspool算法所用到的壞字符策略

 1     /**
 2      * 壞字符策略
 3      */
 4     private void horspoolInitocc() {
 5         int j;
 6         char a;
 7 
 8         for (a = 0; a < alphabetSize; a++)
 9             occ[a] = -1;
10 
11         for (j = 0; j < m - 1; j++) {
12             a = p[j];
13             occ[a] = j;
14         }
15     }

  分析:預處理階段為O(m + σ)時間復雜度和O(σ)空間復雜度。

Horspool算法的搜索函數

 1     /**
 2      * Horspool算法的搜索函數
 3      */
 4     private void horspoolSearch() {
 5         int i = 0, j;
 6         while (i <= n - m) {
 7             j = m - 1;
 8             while (j >= 0 && p[j] == t[i + j]) j--;
 9             if (j < 0) report(i);
10             i += m - 1;
11             i -= occ[t[i]];
12         }
13     }

  搜索階段具有二次最壞情況O(mn),但是可以證明,一個文本字符的平均比較數在1σ 2 /(σ+ 1)之間。

四、總結

  BM算法中的壞字符策略對於σ比較小的來說不是很有效,但適合當σ與模式的長度相比比較大時。當ASCII表和在文本編輯器下進行的常規搜索一樣BMH變得非常有用。在實踐中,單獨使用它會產生非常有效的算法。 Horspool建議僅使用窗口最右邊字符的壞字符移位來計算Boyer-Moore算法中的移位。

源代碼:

  1 package algorithm;
  2 
  3 public class Horspool {
  4     private static int alphabetSize = 256;
  5     private char[] p, t;        // 模式,文本
  6     private int m, n;           // 模式的長度,文本的長度
  7     private int[] occ;          // 記錄文本字符在模式中的位置
  8     private String matches;     // 匹配位置
  9     private char[] showmatches; // 顯示匹配的字符數組
 10 
 11     public Horspool() {
 12         occ = new int[alphabetSize];
 13     }
 14 
 15     public void search(String tt, String pp) {
 16         setText(tt);
 17         setPatten(pp);
 18         horspoolSearch();
 19     }
 20 
 21     /**
 22      * 設置文本
 23      *
 24      * @param tt
 25      */
 26     private void setText(String tt) {
 27         n = tt.length();
 28         t = tt.toCharArray();
 29         initMatches();
 30     }
 31 
 32     /**
 33      * 設置模式
 34      *
 35      * @param pp
 36      */
 37     private void setPatten(String pp) {
 38         m = pp.length();
 39         p = pp.toCharArray();
 40         horspoolInitocc();
 41     }
 42 
 43     /**
 44      * 壞字符策略
 45      */
 46     private void horspoolInitocc() {
 47         int j;
 48         char a;
 49 
 50         for (a = 0; a < alphabetSize; a++)
 51             occ[a] = -1;
 52 
 53         for (j = 0; j < m - 1; j++) {
 54             a = p[j];
 55             occ[a] = j;
 56         }
 57     }
 58 
 59     /**
 60      * Horspool算法的搜索函數
 61      */
 62     private void horspoolSearch() {
 63         int i = 0, j;
 64         while (i <= n - m) {
 65             j = m - 1;
 66             while (j >= 0 && p[j] == t[i + j]) j--;
 67             if (j < 0) report(i);
 68             i += m - 1;
 69             i -= occ[t[i]];
 70         }
 71     }
 72 
 73     /**
 74      * 初始化匹配位置該顯示的數組
 75      */
 76     private void initMatches() {
 77         matches = "";
 78         showmatches = new char[n];
 79         for (int i = 0; i < n; i++) {
 80             showmatches[i] = ' ';
 81         }
 82     }
 83 
 84     /**
 85      * 匹配報告
 86      *
 87      * @param i
 88      */
 89     private void report(int i) {
 90         matches += i + " ";
 91         showmatches[i] = '^';
 92     }
 93 
 94     /**
 95      * 搜索后返回匹配位置
 96      *
 97      * @return
 98      */
 99     public String getMatches() {
100         return matches;
101     }
102 
103     /**
104      * BMH測試主函數
105      *
106      * @param args
107      */
108     public static void main(String[] args) {
109         Horspool horspool = new Horspool();
110         String tt, pp;
111         tt = "abcdabcd";
112         pp = "abc";
113         horspool.search(tt, pp);
114         System.out.println(pp);
115         System.out.println(tt);
116         System.out.println(horspool.showmatches);
117         System.out.println(horspool.getMatches());
118     }
119 }
View Code


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM