AC自動機多模匹配算法 C++實現

AC自動機多模匹配算法 C++實現

相關內容

字典樹TrieC++實現
KMP算法C++實現

AC自動機

Aho-Corasick automaton,是一種多模板匹配算法,基於Trie樹和KMP算法,查找操作與Trie類似,與Trie不同的是,AC自動機查找過程中失配時,到當前失配結點fail指針指向的結點繼續查找,fail指針指向當前最結尾字符串的最長後綴子串,每次遇到一個結點檢測其是否爲單詞的尾結點,若是則輸出該節點,若失配則還要檢測fail指針指向的結點是否爲某個單詞的尾結點,失配後轉向最長後綴的下一個結點,保證t串不用回溯

Fail數組

其中getFail()函數的else語句中的letters[currentLevel][i] = letters[fail[currentLevel]][i];,方便了search()函數中向下查找的操作,即currentLevel = letters[currentLevel][letter];

void AhoCorasickAutomaton::getFail() {
	// 以BFS順序計算fail數組指向的結點
    queue<int> q;
    fail[0] = 0;
    // 將第0行出現的所有結點入隊
    for (int i = 0; i < SIGMA_SIZE; i++) {
        int letter = letters[0][i];
        if (letter) {
            fail[letter] = 0;
            q.push(letter);
        }
    }

    while (!q.empty()) {
    	// 隊首元素作爲當前字母對應層
        int currentLevel = q.front();
        q.pop();
        for (int i = 0; i < SIGMA_SIZE; i++) {
        	// 對於當前字母結點的所有子結點
            int letter = letters[currentLevel][i];
            if (letter) {
            	// 將存在的結點對應層入隊,求解其孩子結點(若存在)的fail指針
                q.push(letter);
            	// 若子節點存在,則其fail指針指向其父結點對應fail指向結點的對應子節點
            	// 若以其當前結點爲結尾的字符串存在大於1的最長後綴兩種情況
            	// 當前結點若在其父結點的fail指向結點的子節點中也存在,則以當前結點爲最長後綴的長度加1,並將fail指針指向它
            	// 當前結點若在其父結點的fail指向結點的子節點中不存在,則fail指針指向個根節點0
                fail[letter] = letters[fail[currentLevel]][i];
            } else {
            	// 爲方便查找操作
            	// 若不存在,當前孩子結點則置爲父結點的fail指針的對應孩子結點
            	// 若對應孩子節點存在,則直接跳轉到對應結點處即可
            	// 若不存在, 則爲0,跳轉到根節點
                letters[currentLevel][i] = letters[fail[currentLevel]][i];
            }
        }
    }
}

查找操作

void AhoCorasickAutomaton::search(string t) {
    int currentLevel = 0;
    for (int i = 0; i < t.size(); i++) {
        int letter = index(t[i]);
        // 向下搜索直到遍歷整個t字符串
        // 若存在對應結點則繼續向下,否則跳轉到對應fail指針
        // 如上述getFail()函數所述
        // getFail()函數的else語句中已經將不存在的結點值置爲對應fail指針的值
        currentLevel = letters[currentLevel][letter];
		// 打印單詞
        if (tag[currentLevel]) {
            cout << information[currentLevel] << endl;
        }
        if (tag[fail[currentLevel]]) {
            cout << information[fail[currentLevel]] << endl;
        }
    }
}

樣例圖解

綠色箭頭指向結點fail指針對應值,紅色結點代表單詞的尾結點

實現代碼

/*
author : eclipse
email  : [email protected]
time   : Sun Jun 14 21:51:51 2020
*/
#include <bits/stdc++.h>
using namespace std;

class AhoCorasickAutomaton {
private:
    static const int MAX_SIZE = 1024;
    static const int SIGMA_SIZE = 26;
    vector<vector<int> > letters;
    vector<string> information;
    int size;
    vector<bool> tag;
    vector<int> fail;
    int index(char c);
public:
    AhoCorasickAutomaton(vector<string> words);
    void insert(string str);
    void search(string str);
    void getFail();
};

AhoCorasickAutomaton::AhoCorasickAutomaton(vector<string> words) {
    size = 0;
    fail.resize(MAX_SIZE);
    tag.resize(MAX_SIZE);
    information.resize(MAX_SIZE);
    letters.resize(MAX_SIZE);
    for (int i = 0; i < MAX_SIZE; i++) {
        letters[i].resize(SIGMA_SIZE);
    }
    for (int i = 0; i < SIGMA_SIZE; i++) {
        letters[0][i] = 0;
    }
    for (int i = 0; i < words.size(); i++) {
        insert(words[i]);
    }
    getFail();
}

int AhoCorasickAutomaton::index(char c) {
    return c - 'a';
}

void AhoCorasickAutomaton::insert(string str) {
    int currentLevel = 0;
    for (int i = 0; i < str.size(); i++) {
        int letter = index(str[i]);
        if (!letters[currentLevel][letter]) {
            for (int j = 0; j < SIGMA_SIZE; j++) {
                letters[size][j] = 0;
            }
            tag[size] = false;
            letters[currentLevel][letter] = size++;
        }
        currentLevel = letters[currentLevel][letter];
    }
    tag[currentLevel] = true;
    information[currentLevel] = str;
}

void AhoCorasickAutomaton::getFail() {
    queue<int> q;
    fail[0] = 0;
    for (int i = 0; i < SIGMA_SIZE; i++) {
        int letter = letters[0][i];
        if (letter) {
            fail[letter] = 0;
            q.push(letter);
        }
    }

    while (!q.empty()) {
        int currentLevel = q.front();
        q.pop();
        for (int i = 0; i < SIGMA_SIZE; i++) {
            int letter = letters[currentLevel][i];
            if (letter) {
                q.push(letter);
                fail[letter] = letters[fail[currentLevel]][i];
            } else {
                letters[currentLevel][i] = letters[fail[currentLevel]][i];
            }
        }
    }
}

void AhoCorasickAutomaton::search(string t) {
    int currentLevel = 0;
    for (int i = 0; i < t.size(); i++) {
        int letter = index(t[i]);
        currentLevel = letters[currentLevel][letter];
        if (tag[currentLevel]) {
            cout << information[currentLevel] << endl;
        }
        if (tag[fail[currentLevel]]) {
            cout << information[fail[currentLevel]] << endl;
        }
    }
}

int main(int argc, char const *argv[]) {
    vector<string> words;
    int N;
    scanf("%d", &N);
    words.resize(N);

    for (int i = 0; i < N; i++) {
        cin >> words[i];
    }

    AhoCorasickAutomaton *ahoCorasickAutomaton = new AhoCorasickAutomaton(words);
    string t;
    cin >> t;
    ahoCorasickAutomaton->search(t);
    return 0;
}

輸入數據

6
his
hers
her
him
he
she
shers

輸出結果

she
he
her
hers

鳴謝

《算法競賽入門經典訓練指南》

最後

  • 由於博主水平有限,不免有疏漏之處,歡迎讀者隨時批評指正,以免造成不必要的誤解!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章