记录:AC自动机

这是对于AC自动机代码的理解,代码是网上找的大佬的,纯属记录。

链接如下:

ac自动机详解 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string.h>
#include <string>
using namespace std;
const int maxn = 1e7 + 5;
const int MAX = 10000000;
int cnt;

struct node {
    node *next[26];//子节点(最多26个)
    node *fail;//指针指向
    int sum;//记录有多少种路径可以到达
};
node *root;
char key[70];
node *q[MAX];
int head, tail;
node *newnode;
char pattern[maxn];
int N;
void Insert(char *s)//向字典树添加节点(建树)
{
    node *p = root;
    for (int i = 0; s[i]; i++)//当单词还没有到末尾时
    {
        int x = s[i] - 'a';
        if (p->next[x] == NULL)//如果还没有添加过这个字母
        {
            newnode = new node;
            for (int j = 0; j<26; j++) newnode->next[j] = 0;//又一轮初始化,将指针归0,将下一个节点归0,将sum归0
            newnode->sum = 0; newnode->fail = 0;
            p->next[x] = newnode;//将新开辟的空间赋给p的下一个节点,边是x,将点移到下一个节点处
        }
        p = p->next[x];//如果添加过这个字母,将点移到下一个节点处
    }
    p->sum++;//此单词的数量加1//同时也表示这个节点是一个单词的末尾
}
void build_fail_pointer()//建立如同KMP的指针指向//基于队列BFS进行实现
{
    head = 0;
    tail = 1;
    q[head] = root;//先将指针指向root节点
    node *p;
    node *temp;
    while (head < tail)
    {
        temp = q[head++];
        for (int i = 0; i <= 25; i++)
        {
            if (temp->next[i])//如果下一个节点存在
            {
                if (temp == root)//如果该节点是根节点
                {
                    temp->next[i]->fail = root;//将指针置于根节点
                }
                else
                {
                    p = temp->fail;//如果该节点不是根节点
                    while (p)//如果指针已经有指向了//不停的跑循环,直到指针所指向的点下没有节点或者下一个节点存在时
                    {
                        if (p->next[i])//如果指针指向的那个节点的下一个节点存在
                        {
                            temp->next[i]->fail = p->next[i];//该节点的下一个节点的指针指向原来指向的节点的下一个节点???如何判断所指向的那串东西是后缀呢???
                            break;
                        }
                        p = p->fail;//将指针放到指向的节点处
                    }
                    if (p == NULL) temp->next[i]->fail = root;//如果指针还没有指向,那么将指针指向根节点
                }
                q[tail++] = temp->next[i];//将指针放到下一层节点处
            }
        }
    }
}
void ac_automation(char *ch)//利用求得的fail指针进行匹配
{
    node *p = root;//先将指针指向根节点
    int len = strlen(ch);
    for (int i = 0; i < len; i++)
    {
        int x = ch[i] - 'a';
        while (!p->next[x] && p != root) p = p->fail;//当p存在下一个节点时,p指到p的下一个匹配点处
        p = p->next[x];//指到x对应那条边连接的节点处
        if (!p) p = root;//如果p是空的,那么指到根节点处
        node *temp = p;
        while (temp != root)
        {
            if (temp->sum >= 0)
            {
                cnt += temp->sum;
                temp->sum = -1;
            }
            else break;
            temp = temp->fail;
        }
    }
}
int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        root = new node;// (struct node *)malloc(sizeof(struct node));
        for (int j = 0; j<26; j++) root->next[j] = 0;//刚开始时的所有节点初始化为0
        root->fail = 0;//刚开始时的所有指针指向初始化为0
        root->sum = 0;//个数初始化为0
        scanf("%d", &N);
        for (int i = 1; i <= N; i++)
        {
            cin>>key;
            Insert(key);
        }
        cin>>pattern;
        cnt = 0;
        build_fail_pointer();
        ac_automation(pattern);
        printf("%d\n", cnt);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_41926958/article/details/82144937
今日推荐