首页 > 其他分享 >AC 自动机

AC 自动机

时间:2024-08-03 20:07:00浏览次数:4  
标签:AC int tr ++ fail 自动机

AC 自动机用于解决多模匹配问题。

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

询问所有模式串在文本串中出现的总次数。

自动机上节点记录 word 表示此处为结尾的单词数。

// 名字空间封装好的 AC 自动机
namespace AC
{
	int tr[maxn][26], tot;
	int word[maxn], fail[maxn];
	/*
	失配指针 fail:
	当前节点所表示字符串的最长真后缀,使得该后缀作为某个单词的前缀出现
	也可以理解为:当前状态指向后缀不变的最长字符串的指针
	还可以理解为:当前状态删掉最少的前缀到达的字符串的指针
	*/
	void insert(char *s) // 就正常插入
	{
		int u = 0;
		for (int i = 1; s[i]; i++)
		{
			int c = s[i] - 'a';
			if (!tr[u][c])
				tr[u][c] = ++tot; // 如果没有则插入新节点
			u = tr[u][c];		   // 搜索下一个节点
		}
		word[u]++; // 尾为节点 u 的串的个数
	}

	void build()
	{
		queue<int> q;
		for (int i = 0; i < 26; i++) // 将深度为 2 的字符插入队列
			if (tr[0][i])
				q.push(tr[0][i]);
		while (q.size()) // bfs
		{
			int u = q.front();
			q.pop();
			for (int i = 0; i < 26; i++)
			{
				int v = tr[u][i]; // 每个循环都是求出其儿子的 fail
				if (v)			  // 若该儿子是真实的
				{
					fail[v] = tr[fail[u]][i]; // 儿子的 fail 指向父亲的 fail 的同类儿子
											  // 另一种版本:我的前世和我父亲的前世在一起
					q.push(v);
					//*********非常容易忘!**********
				}
				else
					tr[u][i] = tr[fail[u]][i]; // 否则儿子直接定义为父亲的 fail 的同类儿子
											   // 我是假的,那就跟我父亲的前世走吧

				/*
				解释:
				虚点没有 fail,其本身就代表了跳 fail 的最终结果
				对每一个虚点,其记录了层数最小的实点,且它跟着父亲走
				*/
			}
		}
	}

	int query(char *t)
	{
		int u = 0, res = 0;
		for (int i = 1; t[i]; i++)
		{
			u = tr[u][t[i] - 'a']; // 向下走一层(是虚点则回溯)
								   // 这一行就干了朴素 ACAM 跳 fail 一大堆干的事情

			for (int j = u; j && word[j] != -1; j = fail[j]) // 不断跳 fail
			{
				res += word[j]; // 路径上的所有状态都在文本串中出现了,注意每个点可能有多个单词
				word[j] = -1;	// 每个单词只能统计一遍

				/*
				word[j]==-1 说明这个单词已经被统计过了,其后的单词也在那一遍遍历中统计过了,因此可以跳出
				一次跳 fail 的过程中保证了一部分后缀不变,跳 fail 的实质就是删掉部分前缀
				*/
			}
		}
		return res;
	}
}

P3796 【模板】AC 自动机(加强版)

找到出现次数最多的单词及其出现次数(可能不止一个)。

记录 word 同前例,取最大值即可,在节点上可以记录初始字符串的序号。最后把同次数的所有单词输出。

P5357 【模板】AC 自动机(二次加强版)

好戏登场。

照理说正常求 cnt 输出即可,但 query 的跳 fail 太慢了。

void query(char *t)
{
	int u = 0;
	for (int i = 1; t[i]; i++)
	{
		u = tr[u][t[i] - 'a'];
		for (int j = u; j; j = fail[j])
			val[j]++;
	}
	for (int i = 0; i <= tot; i++)
	{
		for (int j : idx[i])
			cnt[j] = val[i];
	}
}

这时我们就要祭出拓扑排序优化啦 ( ̄▽ ̄)/

注意到每次跳 fail 其实是做一个类似于前缀和的操作,那我们就可以预先操作先访问到的节点,最后一并求和,那么效率就会优化。

int in[maxn]; // 入度
int tp[maxn], tt;
void topsort()
{
	for (int i = 1; i <= tot; i++) in[fail[i]]++;
	queue<int> q;
	for (int i = 0; i <= tot; i++)
		if (!in[i]) q.push(i);
	while (!q.empty())
	{
		int u = q.front(), v = fail[u];
		q.pop();
		tp[++tt] = u;
		in[v]--;
		if (!in[v]) q.push(v);
	}
}
void query(char *t)
{
	int u = 0;
	for (int i = 1; t[i]; i++)
	{
		u = tr[u][t[i] - 'a'];
		val[u]++;
	}
	for (int i = 1; i <= tt; i++)
	{
		int x = tp[i];
		val[fail[x]] += val[x];
	}
	for (int i = 0; i <= tot; i++)
	{
		for (int j : idx[i])
			cnt[j] = val[i];
	}
}

P5829 【模板】失配树

求字符串长度为 \(x\) 的前缀和长度为 \(y\) 的前缀的最长公共 border

border:公共前后缀。

这是一道番外题,但与上例的思想共通。

首先肯定是要 KMP,求出 nxt 即为该前缀的 border

一种朴素的做法是每次跳 \(nxt[i] \leftarrow nxt[nxt[i]]}\),但这样显然会被卡。

发现 nxt 在迭代的过程中单调递减,且最终会跳到 \(0\),这样则构成一条链结构。

两条链结构在交叉后不会再分开,符合树形结构。于是我们把 nxt 构成一棵树,答案就是 lca。

这与 AC 自动机有什么关系呢?注意看 fail 数组的构建过程,也同样符合该特征,这说明 fail 也同样构成一棵树。这带来很多有趣的性质。

P3966 [TJOI2013] 单词

板子题。求出 cnt 数组输出,记得拓扑优化建图。

P3121 [USACO15FEB] Censoring G

我们已经讨论过只有一个模式串的情况,现在看看多个单词怎么做。

一个模式串是栈 + KMP,多个模式串自然想到栈 + AC 自动机,匹配成功的时候弹出等同于模式串长个字符,并把 \(u\) 还原到新的栈顶的匹配位置。

理清思路代码才好写。

标签:AC,int,tr,++,fail,自动机
From: https://www.cnblogs.com/tai-chi/p/18340979

相关文章

  • 使用Adobe Acrobat Pro DC 把彩色PDF图像改为黑白PDF,且不改变原图像尺寸。
    一、背景:1.编辑要求你在投稿时确定:Informationaboutcolorfiguresasbeingintendedforprintedcolorreproductionortobeprintedinblack-and-white.2.你的图像是彩色的PDF,而且图像的尺寸与A4纸大小不一致.二、解决办法:1. 把你的彩色PDF通过虚拟打印......
  • react、vue组件编译区别&template解析原理
    react、vue组件打包编译为js时的区别1.react组件打包为js后,jsx会被编译为React.createElement.比如:antd的button.js(函数式组件直接returnjsx)constInternalButton=(props,ref)=>{//React.createElement第三个参数children一般兼容传数组和分开多个参数传递俩种形式......
  • thinkphp连接Oracle
    1、连接准备(自行下载对应版本)PHP驱动扩展 :用于PHP连接OracleOracle即时客户端 :Oracle即时客户端,用于与Oracle通信,必须匹配Oracle版本VC运行库 :不一定安装,服务器中有运行库就不用安装 2、扩展安装php.ini中extension=oci8_12cextension=pdo_oci一般在配置文件中已存在......
  • 【详细版】Spring Tips: Spring Statemachine
    SpringTips:SpringStatemachine大纲引言介绍SpringStateMachine及其重要性解释状态机的基本概念和用途SpringStateMachine概述状态机的定义和功能状态机的应用场景SpringStateMachine的DSL和特性创建和配置SpringStateMachine使用start.spring.io创建......
  • 常回家看看之tcachebin-attack
    常回家看看之tcachebin-attack自从glibc2.26之后出现了新的堆管理机制,及引用了tcachebin机制,tcachebin也是主要分配小堆块的,有40条bin链(0x10-0x410)那么这样的分配有很多和smallbin和fastbin重叠的部分,及malloc申请之后free掉的小堆块优先进入tcachebin中,这样的分配减小的分配......
  • react-hook-form验证
    import{useForm}from"react-hook-form";import{zodResolver}from"@hookform/resolvers/zod";importi18nextfrom"i18next";import{z}from"zod";import{zodI18nMap}from"zod-i18n-map";//Impor......
  • 常回家看看之fastbin_attack
    常回家看看之fastbin_attack原理分析fastbin属于小堆块的管理,这里说的fastbin_attack大多指glibc2.26之前的手法,因为自glibc2.26以后,glibc迎来了一位新成员tcachebin,它减少了堆的开销,使堆管理变得迅速而高效,而且申请的小堆块会优先进入tachebin中,只有tachebin其中一个链表满了再......
  • Caused by: java.lang.ClassNotFoundException:org.apache.hadoop.hive.conf.hiveConf
    在sqoop执行create-hive-table时候报错这样,java.io.IOException:原因是缺失jar包,可能是sqoop conf文件的sqoop-env-template.sh里面没有配置相关的hadoop hivezookeeper 的相关环境变量进入sqoop的conf文件下找到sqoop-env-template.sh进入添加相关得到环境变量(注意......
  • 龙国南方航空滑块acw_v2+cookie+风控处理+type后缀
    ​声明本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!           本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技......
  • 【思科模拟器Packet Tracer的一些操作】你见过这样PacketTracer吗
    你见过这样PacketTracer吗?机柜抓包模拟城域网各位网工朋友应该都用过思科模拟器吧PacketTracer是思科系统开发的一款网络模拟器,用于模拟计算机网络中的设备和网络环境。它可以帮助网络工程师或学生在没有真实设备的情况下学习和实验各种网络配置和协议。Pac......