1. 程式人生 > 實用技巧 >如何實現文字編輯器中的查詢替換功能?——BF演算法

如何實現文字編輯器中的查詢替換功能?——BF演算法

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;
}