AC自动机

AC自动机

简介

AC自动机通常用来求n个模式串s[i]有多少个不同的模式在文本串t里出现过。前置知识:kmp,字典树。

AC自动机是的代码基本步骤:

  • 第一步,建一颗字典树来存储s数组
  • 第二步,构建失配指针。找到两个子节点,按照它们的父亲向上找。一直找到祖先一样,就可以返回了。
  • 第三步匹配。首先,我们用一个指针指向根节点,接着,读入单词,检查是否存在这个子节点,然后指针跳转到子结点,如果不存在,就直接跳到失配指针即可。

代码

P3808 【模板】AC 自动机(简单版)
这体就是一道AC自动机的模板题。
第一步:建树,用字典树来储存这个结构

void insert(string& s){//建树
    int u=0;
    for(int i=0; s[i]; i++){
        int v=s[i]-'a';
        if(!trie[u].son[v]){//还没有被建过
        //if(trie[u].son[v]==0) 说明这个节点还没有被建造过,需要用一个新的数字作为下标tot+1来储存
            trie[u].son[v]=++tot;//为这个不同的子节点来建立新的节点
        }
        u=trie[u].son[v];
    }
    trie[u].cnt++;
}

第二步:构建失配指针

void getfail(){
    queue<int> q;
    for(int v=0; v<26; v++){
        int c=trie[0].son[v];
        if(c){
            trie[c].fail=0;
            q.push(c);
        }
    }
    while(!q.empty()){
        int u=q.front();
        q.pop();
        int f=trie[u].fail;
        for(int v=0; v<26; v++){
            int c=trie[u].son[v];
            if(c){
                trie[c].fail=trie[f].son[v];
                q.push(c);
            }
            else{
                trie[u].son[v]=trie[f].son[v];
            }
        }
    }
}

第三步:匹配

int find(string& s){
    int u=0;
    int sum=0;
    for(int i=0; s[i]; i++){
        int v=s[i]-'a';
        int c=trie[u].son[v];
        while(c && trie[c].cnt!=-1){
        //while(c!=0 && trie[c].cnt!=-1)
            sum+=trie[c].cnt;
            trie[c].cnt=-1;
            c=trie[c].fail;
        }
        u=trie[u].son[v];
    }
    return sum;
}

完整代码

#include<queue>
#include<vector>
#include<iostream>
#define endl "\n"
//0 false
using namespace std;

struct mktr{
    int son[30];//儿子只有可能是A到Z26个字符,所以数组只需要开到30就肯定够
    int cnt,fail;
}trie[500010];

int tot=0;

void insert(string& s){//建树
    int u=0;
    for(int i=0; s[i]; i++){
        int v=s[i]-'a';
        if(!trie[u].son[v]){//还没有被建过
        //if(trie[u].son[v]==0) 说明这个节点还没有被建造过,需要用一个新的数字作为下标tot+1来储存
            trie[u].son[v]=++tot;//为这个不同的子节点来建立新的节点
        }
        u=trie[u].son[v];
    }
    trie[u].cnt++;
}

void getfail(){
    queue<int> q;
    for(int v=0; v<26; v++){
        int c=trie[0].son[v];
        if(c){
            trie[c].fail=0;
            q.push(c);
        }
    }
    while(!q.empty()){
        int u=q.front();
        q.pop();
        int f=trie[u].fail;
        for(int v=0; v<26; v++){
            int c=trie[u].son[v];
            if(c){
                trie[c].fail=trie[f].son[v];
                q.push(c);
            }
            else{
                trie[u].son[v]=trie[f].son[v];
            }
        }
    }
}

int find(string& s){
    int u=0;
    int sum=0;
    for(int i=0; s[i]; i++){
        int v=s[i]-'a';
        int c=trie[u].son[v];
        while(c && trie[c].cnt!=-1){
        //while(c!=0 && trie[c].cnt!=-1)
            sum+=trie[c].cnt;
            trie[c].cnt=-1;
            c=trie[c].fail;
        }
        u=trie[u].son[v];
    }
    return sum;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    //cout.tie(0);
    int n;
    string s;
    cin>>n;
    for(int i=0; i<n; i++){
        cin>>s;
        insert(s);//建树
    }
    getfail();
    cin>>s;
    cout<<find(s)<<endl;
    return 0;
}
posted @   lwr2010  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示