洛谷--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

扫描二维码关注公众号,回复: 7679695 查看本文章

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\n",ans);
    return 0;
}

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

猜你喜欢

转载自www.cnblogs.com/acmer-hmin/p/11761622.html