如何實現文字編輯器中的查詢替換功能?——BF演算法
阿新 • • 發佈:2020-08-09
1 BM(Boyer-Moore)演算法
它是一種非常高效的字串匹配演算法,有實驗統計,它的效能是著名的KMP演算法的 3 到 4 倍。BM 演算法核心思想是,利用模式串本身的特點,在模式串中某個字元與主串不能匹配的時候,將模式串往後多滑動幾位,以此來減少不必要的字元比較,提高匹配的效率。BM 演算法構建的規則有兩類,壞字元規則和好字尾規則。好字尾規則可以獨立於壞字元規則使用。因為壞字元規則的實現比較耗記憶體,為了節省記憶體,我們可以只用好字尾規則來實現 BM 演算法。
1.1 壞字元規則
BM 演算法的匹配順序比較特別,它是按照模式串下標從大到小的順序,倒著匹配的。我畫了一張圖,你可以看下。我們從模式串的末尾往前倒著匹配,當我們發現某個字元沒法匹配的時候。我們把這個沒有匹配的字元叫作壞字元(主串中的字元)。
1.2 好字尾規則
好字尾規則實際上跟壞字元規則的思路很類似。你看我下面這幅圖。當模式串滑動到圖中的位置的時候,模式串和主串有 2 個字元是匹配的,倒數第 3 個字元發生了不匹配的情況。
好字尾的處理規則中最核心的內容:
-
在模式串中,查詢跟好字尾匹配的另一個子串;
-
在好字尾的字尾子串中,查詢最長的、能跟模式串字首子串匹配的字尾子串;
2 各部分程式碼如下
#include <iostream> #include <math.h> #include <cstring> using namespace std; #define MAXNUM 256 // 計算壞字元對應的hashtable void generateHashTable(char b[], int m, int hashtable[]){ for(int i = 0; i < m; i++){ int ascii = (int)b[i]; // 這樣寫是可以的,會將壞字元放在最右邊兒的位置 hashtable[ascii] = i; } } int moveByGS(int j, int m, int suffix[], bool 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]) return r; } return m; } // 計算好字尾對應的陣列 void generateGS(char b[], int m, int suffix[], bool prefix[]){ // b[0, i] for(int i = 0; i < m - 1 ; i++){ int j = i; // 公共字尾串長度 int k = 0; // 妙啊 while(j >= 0 && b[j] == b[m - 1 - k]){ j--; k++; suffix[k] = j + 1; } if(j == -1) prefix[k] = true; } } // BF演算法 int boyerMoore(char a[], int n, char b[], int m){ // 根據模式串生成hashtable int hashtable[MAXNUM + 1]; // 初始化 memset(hashtable, -1, sizeof(hashtable)); generateHashTable(b, m, hashtable); // 根據模式串生成suffix、prefix int suffix[MAXNUM + 1]; bool prefix[MAXNUM + 1]; memset(suffix, -1, sizeof(suffix)); memset(prefix, false, sizeof(prefix)); generateGS(b, m, suffix, prefix); int i = 0; while(i <= n - m){ int j; // BF演算法從後往前匹配 for(j = m - 1; j >= 0; j--){ // 找到壞字元 if(a[i + j] != b[j]) break; } if(j < 0) return i; // 找到壞字元在模式串中的位置 int x = j - hashtable[(int)a[i+j]]; /***********好字尾方法************/ int y = 0; // 如果有好字尾 if(j < m - 1) y = moveByGS(j, m, suffix, prefix); i = i + max(x, y); } return -1; }