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

摘要:
Portal AC自动机是一种优化的多模式字符串匹配算法,它类似于trie树和KMP的组合。该算法分为三个部分:建立trie树、查找故障和匹配。假设有几个词:cod、cos、ost、op。基于普通的trie树,模式字符串的失配函数像kmp一样处理。创建trie树与创建公共trie相同。每次到达一个节点时,以该节点结尾的数字将包含在答案中,模式字符串将继续跳转到失败。因为可能会形成环,所以需要记录当前节点是否已被访问,以防止重复。trie[j]。vis;j=trie[j].fail]){ans+=trie[j].fin;trie[j].vis=true;}}返回者;}我还能想到什么?完整的代码如下:#include#include#include#include#defineMogeKoqwq#includeusingspacestd;组分最大值=1e6+10;整数,num;字符[maxn];结构节点{intson[26];intfin,失败;boolvis;}trie[maxn];voidinsert{intlen=strlen;intu=0;对于{intv=s[i]-'a';if(!

传送门

AC自动机(Aho-Corasick automaton)是一种优化的多模式串匹配的算法,它像是trie树和KMP的结合体。

这个算法分为三部分:建立trie树,求fail,匹配。

Luogu P3808 【模板】AC自动机(简单版)第1张

假设有cod,cos,ost,op几个单词。(这图画了好久好久好久)

在普通trie树的基础上,像kmp一样处理出模式串的失配函数。(没有画出的,fail指向根0)

建立trie树

和普通的trie相同。

void insert(char *s) {
    int len = strlen(s);
    int u = 0;
    for(int i = 0; i < len; i++) {
        int v = s[i]-'a';
        if(!trie[u].son[v]) trie[u].son[v] = ++num;
        u = trie[u].son[v];
    }
    trie[u].fin++;
}

 

求fail

用bfs的方法,每次遍历'a'-'z',并将存在的节点压入队列。

  • trie上第一层fail = 0
  • x的son[i]的fail = x的fail的son[i]
  • 如果现在x不存在son[i],那么x的son[i] = x的fail的son[i]

根据上述规则,得到以下代码

void getf() {
    queue <int> q;
    for(int i = 0; i < 26; i++) {
        if(trie[0].son[i]) {
            trie[trie[0].son[i]].fail = 0;
            q.push(trie[0].son[i]);
        }
    }
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        for(int i = 0; i < 26; i++) {
            if(trie[u].son[i]) {
                trie[trie[u].son[i]].fail = trie[trie[u].fail].son[i];
                q.push(trie[u].son[i]);
            } else
                trie[u].son[i] = trie[trie[u].fail].son[i];
        }
    }
}

匹配

本题中,要记录文本串中出现的模式串的数量。

和kmp类似。每走到一个节点,将以这个节点为结尾的数量计入答案,并将模式串不断跳到fail。

因为可能会形成环,需要记录是否访问过当前节点,防止重复。

int query(char *s) {
    int len = strlen(s);
    int u = 0;
    int ans = 0;
    for(int i = 0; i < len; i++) {
        int v = s[i]-'a';
        u = trie[u].son[v];
        for(int j = u; j && !trie[j].vis; j = trie[j].fail) {
            ans += trie[j].fin;
            trie[j].vis = true;
        }
    }
    return ans;
}

还有什么暂时想不到了

完整代码如下

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define MogeKo qwq
#include<queue>
using namespace std;

const int maxn = 1e6+10;
int n,num;
char s[maxn];

struct node {
    int son[26];
    int fin,fail;
    bool vis;
} trie[maxn];

void insert(char *s) {
    int len = strlen(s);
    int u = 0;
    for(int i = 0; i < len; i++) {
        int v = s[i]-'a';
        if(!trie[u].son[v]) trie[u].son[v] = ++num;
        u = trie[u].son[v];
    }
    trie[u].fin++;
}

void getf() {
    queue <int> q;
    for(int i = 0; i < 26; i++) {
        if(trie[0].son[i]) {
            trie[trie[0].son[i]].fail = 0;
            q.push(trie[0].son[i]);
        }
    }
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        for(int i = 0; i < 26; i++) {
            if(trie[u].son[i]) {
                trie[trie[u].son[i]].fail = trie[trie[u].fail].son[i];
                q.push(trie[u].son[i]);
            } else
                trie[u].son[i] = trie[trie[u].fail].son[i];
        }
    }
}

int query(char *s) {
    int len = strlen(s);
    int u = 0;
    int ans = 0;
    for(int i = 0; i < len; i++) {
        int v = s[i]-'a';
        u = trie[u].son[v];
        for(int j = u; j && !trie[j].vis; j = trie[j].fail) {
            ans += trie[j].fin;
            trie[j].vis = true;
        }
    }
    return ans;
}

int main() {
    scanf("%d",&n);
    for(int i = 1; i <= n; i++) {
        scanf("%s",s);
        insert(s);
    }
    getf();
    scanf("%s",s);
    printf("%d",query(s));
    return 0;
}

免责声明:文章转载自《Luogu P3808 【模板】AC自动机(简单版)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇PB TreeView控件鼠标经过时背景颜色变化下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

flask-apscheduler重复执行两次函数

flask-apscheduler 使用方法:1.安装flask-apscheduler 2.实例化并绑定app 3.config.py 配置文件设置:id就是这个任务的编号,func 是需要执行的函数。args是函数需要的参数。trigger 有3种:date(一次性任务),cron(定时任务),interval(循环任务)interval循环间隔调度,...

[C++ STL] vector使用详解

一、概述 vector(向量): 是一种序列式容器,事实上和数组差不多,但它比数组更优越。一般来说数组不能动态拓展,因此在程序运行的时候不是浪费内存,就是造成越界。而vector正好弥补了这个缺陷,它的特征是相当于可分配拓展的数组(动态数组),它的随机访问快,在中间插入和删除慢,但在末端插入和删除快。 二、定义及初始化 使用之前必须加相应容器的头文件: #...

C++库文件解析(conio.h)

转载:https://blog.csdn.net/ykmzy/article/details/51276596 Conio.h 控制台输入输出库该文内容部分参照百度百科 Conio.h 在C stanard library,ISO C 和POSIX标准中均没有定义。Conio 是Console Input/Output的简写,其中定义了通过控制台进行数据输...

(转)C++类所占内存大小计算

C++类所占内存大小计算 转载时请注明出处和作者联系方式文章出处:http://blog.csdn.net/chenchong08作者联系方式:vision_chen@yeah.net 说明:笔者的操作系统是32位的。 class A {}; sizeof( A ) = ?sizeof( A ) = 1明明是空类,为什么编译器说它是1呢?空类同样可以实例化...

winform使用Barcodex控件预览和打印一维码

1、控件下载。   http://files.cnblogs.com/files/masonblog/barcodex.zip 。   包含barcodex.ocx控件、barcodex帮助文档、两个winform控件的dll文件。 2、控件的注册。 (1)检测控件是否注册(方法不唯一)。   本例使用的是判断注册表中 HKEY_CLASSES_ROOTT...

C#程序员开发WinForm必须知道的 Window 消息大全(转)

消息,就是指Windows发出的一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。 消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。例如,对于单击鼠标所产生的消息来说,这个记录中包含了单击鼠标时的坐标。这个记录类型叫做TMsg,它在Windows...