zoukankan      html  css  js  c++  java
  • 【AC自动机】多模匹配算法

    写了个模板题,加强版借鉴大佬的代码,前置技能KMP(感觉没啥用主要是思想),字典树。

    P3808 【模板】AC自动机(简单版)

    
    #include <queue>
    #include <cstdlib>
    #include <cmath>
    #include <cstdio>
    #include <string>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const int maxn =  2*1e6+9;
    
    int trie[maxn][26]; //字典树
    int cntword[maxn];  //记录该单词出现次数
    int fail[maxn];     //失败时的回溯指针
    int cnt = 0;
    
    void insertWords(string s){
        int root = 0;
        for(int i=0;i<s.size();i++){
            int next = s[i] - 'a';
            if(!trie[root][next])
                trie[root][next] = ++cnt;
            root = trie[root][next];
        }
        cntword[root]++;      //当前节点单词数+1
    }
    void getFail(){
        queue <int>q;
        for(int i=0;i<26;i++){      //将第二层所有出现了的字母扔进队列
            if(trie[0][i]){
                fail[trie[0][i]] = 0;
                q.push(trie[0][i]);
            }
        }
    
    //fail[now]    ->当前节点now的失败指针指向的地方
    ////tire[now][i] -> 下一个字母为i+'a'的节点的下标为tire[now][i]
        while(!q.empty()){
            int now = q.front();
            q.pop();
    
            for(int i=0;i<26;i++){      //查询26个字母
                if(trie[now][i]){
                    //如果有这个子节点为字母i+'a',则
    //让这个节点的失败指针指向(((他父亲节点)的失败指针所指向的那个节点)的下一个节点)
                    //有点绕,为了方便理解特意加了括号
    
                    fail[trie[now][i]] = trie[fail[now]][i];
                    q.push(trie[now][i]);
                }
                else//否则就让当前节点的这个子节点
                    //指向当前节点fail指针的这个子节点
                    trie[now][i] = trie[fail[now]][i];
            }
        }
    }
    
    
    int query(string s){
        int now = 0,ans = 0;
        for(int i=0;i<s.size();i++){    //遍历文本串
            now = trie[now][s[i]-'a'];  //从s[i]点开始寻找
            for(int j=now;j && cntword[j]!=-1;j=fail[j]){
                //一直向下寻找,直到匹配失败(失败指针指向根或者当前节点已找过).
                ans += cntword[j];
                cntword[j] = -1;    //将遍历国后的节点标记,防止重复计算
            }
        }
        return ans;
    }
    
    int main() {
        int n;
        string s;
        cin >> n;
        for(int i=0;i<n;i++){
            cin >> s ;
            insertWords(s);
        }
        fail[0] = 0;
        getFail();
        cin >> s ;
        cout << query(s) << endl;
        return 0;
    }
    

    P3796 【模板】AC自动机(加强版)

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<algorithm>
    using namespace std;
    struct Tree//字典树
    {
        int fail;//失配指针
        int vis[26];//子节点的位置
        int cnt;//标记以这个节点结尾的单词编号
    } AC[100000]; //Trie树
    int cnt=0;//Trie的指针
    
    struct Result
    {
        int num;
        int pos;
    } Ans[100000]; //所有单词的出现次数
    
    bool operator <(Result a,Result b)
    {
        if(a.num!=b.num)
            return a.num>b.num;
        else
            return a.pos<b.pos;
    }
    
    string s[100000];
    inline void Clean(int x)
    {
        memset(AC[x].vis,0,sizeof(AC[x].vis));
        AC[x].fail=0;
        AC[x].cnt=0;
    }
    
    void Build(string s,int Num)
    {
        int l=s.length();
        int now=0;//字典树的当前指针
        for(int i=0; i<l; ++i) //构造Trie树
        {
            if(AC[now].vis[s[i]-'a']==0)//Trie树没有这个子节点
            {
                AC[now].vis[s[i]-'a']=++cnt;//构造出来
                Clean(cnt);
            }
            now=AC[now].vis[s[i]-'a'];//向下构造
        }
        AC[now].cnt=Num;//标记单词结尾
    }
    
    void Get_fail()//构造fail指针
    {
        queue<int> Q;//队列
        for(int i=0; i<26; ++i) //第二层的fail指针提前处理一下
        {
            if(AC[0].vis[i]!=0)
            {
                AC[AC[0].vis[i]].fail=0;//指向根节点
                Q.push(AC[0].vis[i]);//压入队列
            }
        }
        while(!Q.empty())//BFS求fail指针
        {
            int u=Q.front();
            Q.pop();
            for(int i=0; i<26; ++i) //枚举所有子节点
            {
                if(AC[u].vis[i]!=0)//存在这个子节点
                {
                    AC[AC[u].vis[i]].fail=AC[AC[u].fail].vis[i];
                    //子节点的fail指针指向当前节点的
                    //fail指针所指向的节点的相同子节点
                    Q.push(AC[u].vis[i]);//压入队列
                }
                else//不存在这个子节点
                    AC[u].vis[i]=AC[AC[u].fail].vis[i];
                //当前节点的这个子节点指向当
                //前节点fail指针的这个子节点
            }
        }
    }
    
    void AC_Query(string s)//AC自动机匹配
    {
        int l=s.length();
        int now=0;
        for(int i=0; i<l; ++i)
        {
            now=AC[now].vis[s[i]-'a'];//向下一层
            for(int t = now; t; t=AC[t].fail) Ans[AC[t].cnt].num++;
        }
    }
    
    int main()
    {
        int n;
        while(cin >> n)
        {
            if(n==0)break;
            cnt=0;
            Clean(0);
            for(int i=1; i<=n; ++i)
            {
                cin>>s[i];
                Ans[i].num=0;
                Ans[i].pos=i;
                Build(s[i],i);
            }
            AC[0].fail=0;//结束标志
            Get_fail();//求出失配指针
            cin>>s[0];//文本串
            AC_Query(s[0]);
            sort(&Ans[1],&Ans[n+1]);
            cout<<Ans[1].num<<endl;
            cout<<s[Ans[1].pos]<<endl;
            for(int i=2; i<=n; ++i)
            {
                if(Ans[i].num==Ans[i-1].num)
                    cout<<s[Ans[i].pos]<<endl;
                else break;
            }
        }
        return 0;
    }
    
    
  • 相关阅读:
    Git 安装配置,key导入
    Android集成支付宝的坑
    RxJava 的使用入门
    RecyclerView 介绍 02 – 重要概念
    AndroidTips:selector的disable状态为什么无效?
    AndroidTips:解决Dialog全屏显示以及Dialog显示自动弹出输入法
    RecyclerView 介绍 01
    python模块-----sqlAlchemy
    python模块-----pyinstaller
    python模块------socket
  • 原文地址:https://www.cnblogs.com/zyysyang/p/11093494.html
Copyright © 2011-2022 走看看