P3808 AC自动机(简单版)

P3808 AC自动机(简单版)

题目描述

给定 \(n\) 个模式串 \(s_i\) 和一个文本串 \(t\),求有多少个不同的模式串在文本串里出现过。
两个模式串不同当且仅当他们编号不同。

输入格式

第一行是一个整数,表示模式串的个数 \(n\)
\(2\) 到第 \((n + 1)\) 行,每行一个字符串,第 \((i + 1)\) 行的字符串表示编号为 \(i\) 的模式串 \(s_i\)
最后一行是一个字符串,表示文本串 \(t\)

输出格式

输出一行一个整数表示答案。

------------------------------------------------------------------------------------

rt,我们需要维护一个AC自动机。
首先我们需要对所以模式串建立一颗Trie,然后将这个文本串放入这颗Trie中匹配

剩下的细节先咕着,因为上课了

code:

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int cnt,tot,n,ans;
char c[N],s[N];
struct Tree{
    int son[26],id,fail,num;
}t[N];
void init()
{
    for(int i=1;i<=cnt;i++)t[i]=Tree{{0},0,1,0};
    cnt=1,ans=0;
}
void add(char c[])//常规Trie插入 
{
    int x=1,len=strlen(c);
    for(int i=0;i<=len-1;i++)
    {
        int to=c[i]-'a';
        if(t[x].son[to])
        {
            t[x].son[to];
        }
        else
        {
            t[x].son[to]=++cnt;
        }
        x=t[x].son[to];
    }
    t[x].num++;
    return ;
}
void getfail()
{
    queue<int> q;
    for(int i=0;i<=25;i++)
    {
        int to=t[1].son[i];
        if(!to)continue;
        q.push(to);
        t[to].fail=1;
    }
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        for(int i=0;i<26;i++)
        {
            int to=t[u].son[i];
            if(to)
            {
                t[to].fail=t[t[u].fail].son[i];//fail:指向to的后缀(即[root->fail]这条路径上的字符串是[toot->to]的一个后缀)
												//显然,fail在Trie上的深度要比to浅(前缀的长度肯定小于等于原串) 
                q.push(to);
            }
            else
            {
                t[u].son[i]=t[t[u].fail].son[i];//如果这个点没有字符,为了方便查询,将这个点连到后缀的儿子上方便匹配 
            }
        }
    }
    return ;
}
void query(char s[])
{
    int len=strlen(s);
    int x=1;
    for(int i=0;i<=len-1;i++)
    {
        int ch=s[i]-'a';
        x=t[x].son[ch];
        for(int j=x;1^j && ~t[j].num;j=t[j].fail)//查询,遍历树上所有的ch的fail指针,匹配对应字符串 
        {
            ans+=t[j].num;
            t[j].num=-1;
        }
    }
}
int main()
{
    //freopen("ac.in","r",stdin);
    //freopen("ac.out","w",stdout);
    cin>>n;
    init();
    for(int i=1;i<=n;i++)
    {
        scanf("%s",c);
        add(c);
    }
    getfail();
    scanf("%s",s);
    query(s);
    printf("%d\n",ans);
    return 0;
}
posted @ 2024-12-06 11:40  liuboom  阅读(17)  评论(0)    收藏  举报