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  : eclipsecs@qq.com
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

鸣谢

《算法竞赛入门经典训练指南》

最后

  • 由于博主水平有限,不免有疏漏之处,欢迎读者随时批评指正,以免造成不必要的误解!
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: AC自动机并没有直接使用KMP匹配算法,但是AC自动机的核心思想可以说是在KMP算法的基础上演化而来的。KMP算法的思想是通过预处理模式串,构建一个状态转移表,然后在匹配时利用状态转移表跳过匹配失败的前缀,从而实现快速匹配AC自动机的思想也是类似的,在预处理模式串的基础上,构建一个有限状态机,然后在匹配文本串的过程中,根据当前字符和状态机的状态转移函数,快速地转移到下一个状态,从而实现快速匹配。因此,可以说AC自动机是KMP算法的一种扩展和升级,它在字符串匹配中具有更加高效和实用的特性。 ### 回答2: AC自动机并没有直接运用KMP匹配算法,但可以说AC自动机是在KMP算法的基础上进行了改进和优化。 KMP(Knuth-Morris-Pratt)算法是一种经典的模式匹配算法,用于在一个主串中查找一个模式串的出现位置。而AC自动机是用于多模匹配算法,可以在一个主串中同时查找多个模式串的出现位置。因此,AC自动机是在KMP算法的基础上进行了扩展和改进。 AC自动机的核心思想是构建一个有限状态自动机,在匹配过程中通过状态的转移来优化匹配效率。它利用了KMP算法中的失配函数的思想,但在实现细节和应用场景上有所不同。 AC自动机通过对所有模式串进行预处理和建立状态转移表,可以在O(n+m)的时间复杂度内完成多模匹配,其中n是主串的长度,m是模式串的平均长度。而KMP算法仅能实现单模式匹配,时间复杂度为O(n+m)。这说明AC自动机在处理多模匹配问题上的效率更高。 因此,尽管AC自动机没有直接运用KMP算法,但它是在KMP算法的基础上发展起来的一种更高效的多模匹配算法。 ### 回答3: AC 自动机并没有直接运用 KMP 匹配算法,但是它的构造和搜索过程中使用了类似的思想。 KMP 算法是一种用于字符串匹配算法,通过预处理模式串来避免不必要的回退,以提高效率。它构建了一个匹配状态机,通过状态转移表来指导搜索过程AC 自动机是在多模式串匹配问题上的一种高效算法。它的核心思想是将所有模式串构建成一棵 Trie 树,并通过添加失败指针(failure link)来实现状态转移。这样,在搜索过程中,每次匹配失败时,不仅会跳转到 Trie 树中的下一个状态,还会根据失败指针跳转到一个更为合适的位置,避免了不必要的回退。 虽然 AC 自动机的构造和搜索过程中使用了和 KMP 算法相似的思想,但它们的应用场景和具体实现方法略有不同。AC 自动机主要用于在一个字符串中同时查找多个模式串的出现位置,而 KMP 算法则是以单个模式串作为输入,找到它在目标字符串中的匹配位置。 综上所述,AC 自动机并没有直接使用 KMP 算法,但在构造和搜索过程中使用了类似的思想,更加高效地解决了多模式串匹配问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值