目录

  • Tria树(前缀树)
    • 介绍
    • 数据结构
    • 插入,搜索,查找
  • AC自动机
    • 介绍
  • 板子题
    • AC代码:
    • 使用指针构建结点但是无法AC的代码

Tria树(前缀树)

介绍

前缀树是一种用于插入查找搜索数据的数据结构,又叫做字典树。后缀树与其类似。和哈希表相比,前缀树不仅可以查找某一个键,也可以查找该键的前缀。并且查找速度只与所要查找的键的字符长度有关。

数据结构

一个只存储小写字母的tria树的数据结构如下:

struct Trienode{bool is_string = false; //是否是结尾Trienode* next[26] = {NULL};
}*root;

插入,搜索,查找

  • 初始化
root = new Trienode();  //初始化
  • 插入
void insert(string word) {Trienode *p = root;for(int i=0;i<word.size();i++){if(p->next[word[i]-'a']==NULL)p->next[word[i]-'a'] = new Trienode();p = p->next[word[i]-'a'];}p->is_string = true;return;
}
  • 查找字符串是否存在
bool search(string word) {Trienode *p = root;for(int i=0;i<word.size();i++){if(p->next[word[i]-'a']==NULL)return false;p = p->next[word[i]-'a'];}return p->is_string;
}
  • 查找前缀是否存在
bool startsWith(string prefix) {Trienode*p =root;for(int i=0;i<prefix.size();i++){if(p->next[prefix[i]-'a']==NULL)return false;p = p->next[prefix[i]-'a'];}return true;
}

AC自动机

介绍

AC自动机,结合了KMP和Tria树。给tria树中的每个结点增加了一个fail指针,当匹配失败时跳转。(找到当前已经有的前缀相等的最长后缀!) 。

  • 构造fail指针的思路如下:

通过bfs(队列)构造fail指针,根结点无fail指针(或者指向空).第一层fail指针指向根节点。当一个结点A遇到字母’a’失配时,他的父亲结点B通过’b’匹配带该结点,找到他父亲的fail指针指向的结点,如果他的父亲的fai指针指向的结点能够匹配’b’到结点C,则结点A的fail结点指向结点C。(B不符合则循环向fail找,一只找不到,则指向根结点)

以单模式串"ababac"为例:(蓝色为fail指针)(相当于kmp算法的AC自动机)

板子题

以一道题目给出写出模版:
P3808 【模板】AC自动机(简单版)

AC代码:

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;const int maxn = 1000006;  //结点个数 struct Aho{struct trienode{int next[26];int fail=-1;int cnt=0; //是否匹配到模式串 }ac[maxn];queue<int> que;int ac_num=1;void init(){    //自动机初始化 for(int i=0;i<maxn;i++){memset(ac[i].next,0,sizeof(ac[i].next));ac[i].fail = ac[i].cnt = 0;}ac[0].fail = -1;ac_num = 1;}void insert(char *word) {int p = 0;int size = strlen(word);for(int i=0;i<size;i++){char c = word[i];if(ac[p].next[c-'a']==0)ac[p].next[c-'a'] = ac_num++;p = ac[p].next[c-'a'];}ac[p].cnt++;return;}void build(){ac[0].fail = -1;que.push(0);while(!que.empty()){int p=que.front();que.pop();for(int i=0;i<26;i++){if(ac[p].next[i]!=0){int p2 = ac[p].fail;while(p2!=-1){if(ac[p2].next[i]!=0){ac[ac[p].next[i]].fail = ac[p2].next[i];break;}p2 = ac[p2].fail;}if(p2==-1) ac[ac[p].next[i]].fail = 0; que.push(ac[p].next[i]);}}}return;}int query(char *words){int n = strlen(words);int res=0;int p = 0;for(int i=0;i<n;i++){char c = words[i];if(ac[p].next[c-'a']!=0)p = ac[p].next[c-'a'];else{p=ac[p].fail;while(p!=-1&&ac[p].next[c-'a']==0)p = ac[p].fail;if(p==-1) p = 0;else p = ac[p].next[c-'a'];}for(int p2=p;p2!=-1&&ac[p2].cnt!=-1;p2=ac[p2].fail){res+=ac[p2].cnt;ac[p2].cnt = -1; //此处是一个优化点! }}return res;}
}aho;char p[1000006];
int main(){int n;scanf("%d",&n);for(int i=0;i<n;i++){   //输入模式串scanf("%s",p);aho.insert(p);}aho.build();scanf("%s",p);printf("%d",aho.query(p));return 0;
}

使用指针构建结点但是无法AC的代码

自己检查了很多遍找不到错误啊啊啊啊啊,把测试样例下载下来数据跑不起来,修改了N遍对照两个代码结果都一样啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊!但是第二个测试样例死活就是过不去啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;struct Aho{struct trienode{trienode* next[26]={NULL};trienode* fail=NULL;int cnt; //是否匹配到模式串 }*root=new trienode();queue<trienode*> que;void insert(char *word) {trienode *p = root;int size = strlen(word);for(int i=0;i<size;i++){if(p->next[word[i]-'a']==NULL)p->next[word[i]-'a'] = new trienode();p = p->next[word[i]-'a'];}p->cnt=1;return;}void build(){while(!que.empty()) que.pop();que.push(root);while(!que.empty()){trienode* p=que.front();que.pop();for(int i=0;i<26;i++){if(p->next[i]!=NULL){trienode* p2 = p->fail;while(p2!=NULL){if(p2->next[i]!=NULL){p->next[i]->fail = p2->next[i];break;}p2 = p2->fail;}if(p2==NULL) p->next[i]->fail = root; que.push(p->next[i]);}}}return;}int query(char *words){int n = strlen(words);int res=0;trienode *p = root;for(int i=0;i<n;i++){char c = words[i];if(p->next[c-'a']!=NULL)p = p->next[c-'a'];else{p=p->fail;while(p!=NULL&&p->next[c-'a']==NULL) p = p->fail;if(p==NULL) p = root;else p = p->next[c-'a'];}for(trienode* p2=p;p2!=NULL&&p2->cnt!=-1;p2=p2->fail){res+=p2->cnt;p2->cnt = -1;    //此处是一个优化点 }}return res;}
}aho;char p[1000006];
int main(){int n;scanf("%d",&n);for(int i=0;i<n;i++){   //输入模式串scanf("%s",p);aho.insert(p);}aho.build();scanf("%s",p);printf("%d",aho.query(p));return 0;
}

Tria树(前缀树)与AC自动机相关推荐

  1. Trie(字典树/前缀树)

    字典树/前缀树 Trie(发音类似 "try")或者说 前缀树(字典树) 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键.这一数据结构有相当多的应用情景,例如自动补完和 ...

  2. 「模拟赛20180306」回忆树 memory LCA+KMP+AC自动机+树状数组

    题目描述 回忆树是一棵树,树边上有小写字母. 一次回忆是这样的:你想起过往,触及心底--唔,不对,我们要说题目. 这题中我们认为回忆是这样的:给定 \(2\) 个点 \(u,v\) (\(u\) 可能 ...

  3. YBTOJ:前缀匹配(AC自动机)

    文章目录 题目描述 解析 代码 题目描述 解析 做的不错 把trie树真的当成一棵树递归即可 注意一个标记时的问题: void AC(){int l=strlen(s0+1),pl=1;for(int ...

  4. 【前缀树】写一个敏感词过滤器

    1.什么是敏感词过滤 这其实是一个很常见的功能,随处可见以至于你可能都没关注过,基本上在有评论的地方都会有它的身影. 举例来说,你打游戏和别人对喷的时候,是不是一些脏话发不出去哈哈,这些词汇会用*** ...

  5. 前缀树介绍,定义,图文详解分析——Java/Kotlin双版本代码

    前缀树 前缀树,又称作字典树,用一个树状的数据结构储存字典中的所有单词. 列,一个包含can.cat.come.do.i.in.inn的前缀树如下图所示: 前缀树是一个多叉树,一个节点可能存在多个节点 ...

  6. python利用Trie(前缀树)实现搜索引擎中关键字输入提示(学习Hash Trie和Double-array Trie)...

    python利用Trie(前缀树)实现搜索引擎中关键字输入提示(学习Hash Trie和Double-array Trie) 主要包括两部分内容: (1)利用python中的dict实现Trie: ( ...

  7. hdu 6096---String(AC自动机)

    题目链接 Problem Description Bob has a dictionary with N words in it. Now there is a list of words in wh ...

  8. AC自动机:多模式串匹配实现敏感词过滤

    文章出处:极客时间<数据结构和算法之美>-作者:王争.该系列文章是本人的学习笔记. 1 敏感词过滤场景 在很多支持用户发表内容的网站,都有敏感词过滤替换的功能.例如将一些淫秽.反动内容过滤 ...

  9. 字符串算法 | AC自动机算法

    1.简介 一种多模式串匹配算法, 可以快速从主串中同时找出所有包含的所有模式串. 对比KMP是单模式匹配, 虽然可以使用单模式串匹配算法逐个进行查找模式串, 但是实际场景中,若模式串的数量可能很大,并 ...

  10. AC自动机原理及代码实现

    目录 一 定义 二 构建字典树 三 构建 AC 自动机 四 模式匹配 五 性能分析 六.代码实现 一 定义 AC 自动机是 KMP 算法和 Trie 树的结合,是经典的多模匹配算法. 首先将多个模式串 ...

最新文章

  1. 校门外的树——树状数组+区间修改
  2. synchronized关键字理解
  3. 云原生势不可挡,华为云GaussDB加速企业数字化转型
  4. 微信又更新了,“拍一拍”玩法升级...
  5. setnx是原子操作吗_Redis面试七连问,你能扛得住吗?
  6. php设计要求,《PHP设计模式介绍》第十章 规范模式
  7. ubuntu 串口调试工具_开源软件分享基于WPF的串口调试工具
  8. Bzoj2527--Poi2011Meteor
  9. Dxg——Bat批处理 开发笔记整理分类合集【所有的相关记录,都整理在此】
  10. python贪吃蛇_python实现贪吃蛇
  11. 图像边缘检测的新方向——量子算法
  12. Office server webs app 集成(JAVA)
  13. 梦参老和尚:糊涂人念〈大悲咒〉往生的故事
  14. ad中电容用什么封装_贴片电容有什么用其中作用有哪些?
  15. Android自定义一个时间轴,通过ListView来实现时间轴的效果
  16. RAP简介教程常用规则
  17. mysql添加一列求乘积_MySQL 生成累计乘积
  18. ## 编写一个从1到150的循环,并在每行打印一个值,另外在每个3的倍数行打印“foo“,在5的倍数行打印“biz“,在10的倍数行打印“baz“;
  19. python getch_python – 是否可以使用getch()来获取不同长度的输入?
  20. android4.4系统 分屏,基于Android系统的宽屏后视镜分屏方法及系统与流程

热门文章

  1. FastApi学习-01
  2. 基于一致性的多无人机协同编队控制——(1)研究现状
  3. jsp与MYSQL注释_JSP的mysql_jdbc驱动程序
  4. 拉扎维模拟CMOS集成电路设计西交张鸿老师课程P10~13视频学习记录
  5. 二、数据组中奇偶数交换
  6. 安全多方计算之计算平均工资
  7. c语言 选择 以下可作为函数fopen中第一个参数的正确格式,可作为函数 fopen 中第一个参数的正确格式?...
  8. CentOS8服务篇2:配置与应用Web服务
  9. php56 gmp,php 源码安装 GMP
  10. Postgresql数据库安装报错