字符串——多模匹配——ac自动机(1.0模板)

推荐博客:https://blog.csdn.net/liu940204/article/details/51347064
https://blog.csdn.net/silence401/article/details/52662605

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

struct Node
{
    int cnt;//是否为单词结尾
    Node *fail;//失败指针
    Node *next[26];
};

struct Node *queue[500005];//队列, BFS构造失败指针时使用

char s[1000005];//主串
char keyword[55];//单词

Node *root;//根节点

void Init(Node *tmp)//初始化结点函数
{
    tmp->cnt = 0;
    tmp->fail = NULL;
    for(int i = 0; i <26; i++)
        tmp->next[i] = NULL;
}

void Build_tire(char *word)// 构建trie函数 或者 插入单词函数
{
    Node *tmp = root;
    int ch;
    int len = strlen(word);

    for(int i =0; i < len; i++)
    {
        ch = word[i] - 'a';
        if(tmp->next[ch] == NULL)//如果该节点所代表的字母 为空 就申请空间构建
        {
            tmp->next[ch] = (struct Node *)malloc(sizeof(Node));
            Init(tmp->next[ch]);
        }

        tmp = tmp->next[ch];//移到ch所在结点

    }

    tmp->cnt++;// 单词结尾cnt++

}

void Build_fail_pointer(Node *root)// fail指针构建函数  本质是bfs实现
{
    int head =0, tail = 0;//队首 队尾

    queue[tail++] = root;//根节点入队

    while(head != tail)
    {
        Node *tmp = NULL;
        Node *tp = queue[head++];//弹出队首

        for(int i = 0; i < 26; i++)
        {
            if(tp->next[i] != NULL)
            {
                if(tp == root)// 第一层 没有子后缀 所以指向root
                {
                    tp->next[i]->fail = root;
                }
                else
                {
                    tmp = tp->fail;

                    while(tmp!= NULL)
                    {
                        if(tmp->next[i]!= NULL)
                        {
                            tp->next[i]->fail = tmp->next[i];
                            break; //
                        }
                        else tmp =tmp->fail;// 缩短后缀
                    }

                    if(tmp == NULL)// 没找到就指向root
                        tp->next[i]->fail = root;
                }

                queue[tail++] = tp->next[i];//处理完了 将他压入队列 ,用于继续处理后面的节点
            }

        }
    }

}

int query(Node *root)
{
    int cnt = 0;
    int ch;
    int len = strlen(s);
    Node *p = root;

    for(int r = 0; r < len; r++)
    {
        ch = s[r]-'a';

        while(p->next[ch]==NULL &&p!= root)// 如果接着上次p点 如果为空 回溯 缩短后缀(相当于l向右)
            p= p->fail;

        p= p->next[ch];
        if(p == NULL)// 为空 表示 没有与该位置s[r] 后缀 相似的前缀
        {
            p = root;
            continue;//直接进行下个
        }


        Node *tmp = p;//
        while(tmp != root)// 回溯过程( 向当于l,聪明的跳)
        {
            if(tmp->cnt >= 0)
            {
                cnt+=tmp->cnt;
                tmp->cnt=-1; // 表示访问过
            }
            else if(tmp->cnt == -1)break; // -1表示从这开始访问过了, 所以就直接结束 循环

            tmp = tmp->fail;// 如果不等于-1 继续回溯(缩短后缀)查询
        }
    }

    return cnt;
}

int main()
{
    int T;
    scanf("%d", &T);

    while(T--)
    {
        root = (Node *)malloc(sizeof(Node));
        Init(root);
        int n;
        scanf("%d", &n);

        for(int i = 0; i < n; i++)
        {
            scanf("%s", keyword);
            Build_tire(keyword);
        }
        Build_fail_pointer(root);

        scanf("%s", s);

        int res = query(root);

        printf("%d\n", res);
    }


    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值