• 洛谷--P3808 【模板】AC自动机(“假的“简单版)


    如果你想要做出这道题,你需要先了解两个知识点:

    1、字典树的构造

    2、KMP算法(也就是fail指针的构造)

    对于字典树,可以看看这个大佬:

    https://www.cnblogs.com/TheRoadToTheGold/p/6290732.html

    对于KMP,可以看看这个大佬:

    https://www.cnblogs.com/SYCstudio/p/7194315.html#4255259 (强烈推荐!!!!)

    代码实现步骤:(前两个步骤是AC自动机的必备)

    1、利用所提供的模式串构造字典树

    2、构造fail指针,即当前节点的fail指针 = 它的父节点fail所指向的节点 所指向的相同子节点。,对于定义fail指针的值,借用queue队列的先进先出的思想(此处为难点,不明白的请到评论区)

    3、依题意构造query函数,返回个数ans

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e6+5;
    char t[maxn],s[maxn];
    struct tree
    {
        int fail;//失配指针
        int son[26];//一个节点最多有26个子节点
        int num;//标记以此节点为结尾
    }DFA[maxn]; //字典树
    int cnt=0;
    void build(string s)
    {
        int now=0;//字典树当前指针
        for(int i=0;i<s.length();++i)
        {
            if(DFA[now].son[s[i]-'a']==0) //树上没有这个子节点
                DFA[now].son[s[i]-'a']=++cnt; //新增一个节点
            now=DFA[now].son[s[i]-'a']; //向下构造
        }
        DFA[now].num+=1;// 标记单词的结尾
    }
    void Get_fail() //构造fail指针
    {
        queue<int> Q;
        for(int i=0;i<26;++i)
        {
            if(DFA[0].son[i]!=0)
            {
                DFA[DFA[0].son[i]].fail=0;//指向根节点,第二层
                Q.push(DFA[0].son[i]); //压入队列
            }
        }
        while(!Q.empty())
        {
            int u=Q.front();
            Q.pop();
            for(int i=0;i<26;++i)  //后面的层
            {
                if(DFA[u].son[i]!=0)//如果存在此节点
                {
                    DFA[DFA[u].son[i]].fail=DFA[DFA[u].fail].son[i];//指向该节点的父节点的fail值对应的节点的相同子节点
                    Q.push(DFA[u].son[i]); //加入队列
                }
                else //如果不存在这个节点
                {
                    DFA[u].son[i]=DFA[DFA[u].fail].son[i];  //当前节点的孩子节点指向当前节点的fail的孩子节点
                }
            }
        }
    }
    int query(char *s)
    {
        int len=strlen(s);
        int now=0,ans=0;
        for(int i=0;i<len;++i)
        {
            now=DFA[now].son[s[i]-'a'];
            for(int t=now;t!=0&&DFA[now].num!=-1;++t)
                {
                    if(DFA[now].num>0) //表示存在结尾
                        ans+=DFA[now].num;
                     DFA[t].num=-1; //标记已访问过
                }
            }
        return ans;
    }
    
    
    int main()
    {
        int n;
        scanf("%d",&n);
        for(int i=0;i<n;i++)
        {
            scanf("%s",t);
            build(t);
        }
        Get_fail();
        scanf("%s",s);
        int ans=query(s);
        printf("%d
    ",ans);
        return 0;
    }

    AC自动机不好理解,原谅我比较懒,没有给大家一个满意的解析,不过大家有问题可以评论区私我,我会尽力为大家解答的!

  • 相关阅读:
    linxu 网络管理
    Linux 命令2
    crontab命令
    Linux 命令
    Linux 命令
    Git命令
    GIT
    Python 修改文件内容
    logging模块
    CSS 选择器
  • 原文地址:https://www.cnblogs.com/acmer-hmin/p/11761622.html
Copyright © 2020-2023  润新知