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

#include
using namespace std;
queue q;
const int N = 500010;
struct AC_automaton
{
    int c[N][26], val[N], fail[N], cnt;
    //c数组记录字典树节点,val数组为该节点是否为字符串结尾(个数)(记录字符串结束的位置), fail记录失配指针, cnt记录节点标号(对应val)
    //cnt作用:模拟动态开点
    void ins(char *s)//向字典树中插入字符串,s为模式串
    {
        int len = strlen(s); int now = 0;//从根节点0开始
        for(int i = 0; i < len; i++)
        {
            int v = s[i] - 'a';//获取当前字符的值
            if(!c[now][v])c[now][v] = ++cnt;//如果这个节点没开,则开点
            now = c[now][v];//now移动到新节点,开始匹配下一个
        }
        val[now]++;//字符串插入完毕后在尾部打标记,val[u]表示以该点结尾的字符串个数
    }
    void build()//获取每个节点的失配指针
    {
        for(int i = 0; i < 26; i++)
            if(c[0][i])fail[c[0][i]] = 0, q.push(c[0][i]);//首先将与根节点相连的点入队,即首字母入队,并赋失配指针为0
        while(!q.empty())
        {
            int u = q.front(); q.pop();//出队
            for(int i = 0; i < 26; i++)
            {
                if(c[u][i])fail[c[u][i]] = c[fail[u]][i], q.push(c[u][i]);//如果该节点存在,则他的失配指针为它的上一个失配指针处的失配指针,并入队
                else c[u][i] = c[fail[u]][i];//该点不存在只更新fail指针
            }
        }
    }
    int query(char *s)//查询文本串中模式串的个数,s为文本串
    {
        int len = strlen(s); int now = 0, ans = 0;//ans记录答案(模式串个数), 从根节点开始
        for(int i = 0; i < len; i++)
        {
            now = c[now][s[i] - 'a'];//从当前节点向下走
            for(int t = now; t && ~val[t]; t = fail[t])ans += val[t], val[t] = -1;//遍历该节点前失配指针,并统计个数,清楚val(避免重复统计)
        }
        return ans;
    }
}AC;
int n;
char p[1000005];
int main()
{
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)scanf("%s", p), AC.ins(p);
    AC.build();
    scanf("%s", p);
    int ans = AC.query(p);
    printf("%d\n", ans);
    return 0;
}

 

你可能感兴趣的:(AC自动机)