【数据结构与算法】字符串匹配 BM算法
BF 算法和 RK 算法
BM 算法和 KMP 算法
Trie 樹和 AC 自動機
BM算法
BM算法的核心思想是通過將模式串沿著主串大踏步的向后滑動,從而大大減少比較次數,降低時間復雜度。而算法的關鍵在于如何兼顧步子邁得足夠大與無遺漏,同時要盡量提高執行效率。這就需要模式串在向后滑動時,遵守壞字符規則與好后綴規則,同時采用一些技巧。
壞字符規則
從后往前逐位比較模式串與主串的字符,當找到不匹配的壞字符時,記錄模式串的下標值si,并找到壞字符在模式串中,位于下標si前的最近位置xi(若無則記為-1),si-xi即為向后滑動距離。(但是壞字符規則向后滑動的步幅還不夠大,于是需要好后綴規則。
好后綴規則
從后往前逐位比較模式串與主串的字符,當出現壞字符時停止。若存在已匹配成功的子串{u},那么在模式串的{u}前面找到最近的{u},記作{u’}。再將模式串后移,使得模式串的{u’}與主串的{u}重疊。若不存在{u’},則直接把模式串移到主串的{u}后面。為了沒有遺漏,需要找到最長的、能夠跟模式串的前綴子串匹配的,好后綴的后綴子串(同時也是模式串的后綴子串)。然后把模式串向右移到其左邊界,與這個好后綴的后綴子串在主串中的左邊界對齊。
何時使用壞字符規則和好后綴規則呢?
首先在每次匹配過程中,一旦發現壞字符,先執行壞字符規則,如果發現存在好后綴,還要執行好后綴規則,并從兩者中選擇后移距離最大的方案執行。
技巧:
1.通過散列表實現,壞字符在模式串中下標位置的快速查詢。
2.每次執行好后綴原則時,都會計算多次能夠與模式串前綴子串相匹配的好后綴的最長后綴子串。為了提高效率,可以預先計算模式串的所有后綴子串,在模式串中與之匹配的另一個子串的位置。同時預計算模式串中(同長度的)后綴子串與前綴子串是否匹配并記錄。在具體操作中直接使用,大大提高效率。
3.如何快速記錄模式串后綴子串匹配的另一個子串位置,以及模式串(相同長度)前綴與后綴子串石否匹配呢?先用一個suffix數組,下標值k為后綴子串的長度,從模式串下標為i(0~m-2)的字符為最后一個字符,查找這個子串是否與后綴子串匹配,若匹配則將子串起始位置的下標值j賦給suffix[k]。若j為0,說明這個匹配子串的起始位置為模式串的起始位置,則用一個數組prefix,將prefix[k]設為true,否則設為false。k從0到m(模式串的長度)于是就得到了模式串所有前綴與后綴子串的匹配情況。
BM算法完整版
// a,b表示主串和模式串;n,m表示主串和模式串的長度。 public int bm(char[] a, int n, char[] b, int m) {int[] bc = new int[SIZE]; // 記錄模式串中每個字符最后出現的位置generateBC(b, m, bc); // 構建壞字符哈希表int[] suffix = new int[m];boolean[] prefix = new boolean[m];generateGS(b, m, suffix, prefix);int i = 0; // j表示主串與模式串匹配的第一個字符while (i <= n - m) {int j;for (j = m - 1; j >= 0; --j) { // 模式串從后往前匹配if (a[i+j] != b[j]) break; // 壞字符對應模式串中的下標是j}if (j < 0) {return i; // 匹配成功,返回主串與模式串第一個匹配的字符的位置}int x = j - bc[(int)a[i+j]];int y = 0;if (j < m-1) { // 如果有好后綴的話y = moveByGS(j, m, suffix, prefix);}//好后綴和壞字符中最長的i = i + Math.max(x, y);}return -1; }// j表示壞字符對應的模式串中的字符下標; m表示模式串長度 private int moveByGS(int j, int m, int[] suffix, boolean[] prefix) {int k = m - 1 - j; // 好后綴長度if (suffix[k] != -1) return j - suffix[k] +1;for (int r = j+2; r <= m-1; ++r) {if (prefix[m-r] == true) {return r;}}return m; }應用
grep命令 文本編輯器查找功能
總結
以上是生活随笔為你收集整理的【数据结构与算法】字符串匹配 BM算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 拼字符串成为时间,和两个计算时间点的中间
- 下一篇: 2021年《职业防治法》宣传周活动资料海