首页 > 其他分享 >【洛谷】P2414 [NOI2011] 阿狸的打字机(AC自动机+离线询问)

【洛谷】P2414 [NOI2011] 阿狸的打字机(AC自动机+离线询问)

时间:2023-03-14 20:14:31浏览次数:82  
标签:AC P2414 int 离线 fail 字符串 include 节点

原题链接

题意

给定 \(n\) 个字符串,\(m\) 次询问一个字符串 \(x\) 在另一个字符串 \(y\) 的出现次数。

\(1 \leq n,m \leq 10^5\)。

思路

要解决多个字符串的问题,不难想到 AC 自动机。

根据 AC 自动机上 fail 数组的性质,即以 \(fail_i\) 所结尾的字符串一定是以 \(i\) 结尾的字符串的后缀。

一个字符串的所有子串可以表示为它所有后缀子串的所有前缀子串,而在 trie 树上,以 \(i\) 结尾的字符串一定是以 \(i\) 子树内任意节点结尾的字符串的前缀。

不难发现,现在问题就转化为了求 trie 数上从根节点到 \(x\) 所对应的结尾节点这条路径上,有多少个节点在 fail 树上是 \(y\) 子树内的节点。

而求一个子树内的节点,就可以用到dfs序。将询问离线,维护从根节点到 \(x\) 节点路径上点的dfs序,也就是单点询问 $$+$ 区间查询,可以用到树状数组。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N=1e5+10;
int c[N],in[N],out[N],num,Q[N],fa[N],cpy[N][26],id[N],tot=1,tr[N][26],fail[N],h[N],ans[N],idx,n,m;char s[N];
vector<int>ask[N];
struct query{int x,y;}q[N];
struct edge{int v,nex;}e[N];
void add(int u,int v){e[++idx]=edge{v,h[u]};h[u]=idx;}
void build_AC()
{
	fail[1]=1;int hh=0,tt=-1;
	for(int i=0;i<26;i++) if(tr[1][i]) Q[++tt]=tr[1][i],fail[tr[1][i]]=1;else tr[1][i]=1;
	while(hh<=tt)
	{
		int u=Q[hh++];
		for(int i=0;i<26;i++)
		{
			int &v=tr[u][i];
			if(!v) v=tr[fail[u]][i];
			else Q[++tt]=v,fail[v]=tr[fail[u]][i];
		}
	}
}
void dfs(int u)
{
	in[u]=++num;
	for(int i=h[u];i;i=e[i].nex) dfs(e[i].v);
	out[u]=num;
}
void update(int x,int k){while(x<=n) c[x]+=k,x+=x&-x;}
int query(int x){int res=0;while(x) res+=c[x],x-=x&-x;return res;}
void solve(int u)
{
	update(in[u],1);
    for(int i=0;i<ask[u].size();i++) ans[ask[u][i]]=query(out[id[q[ask[u][i]].x]])-query(in[id[q[ask[u][i]].x]]-1);
    for(int i=0;i<26;i++) if(cpy[u][i]) solve(cpy[u][i]);
	update(in[u],-1);
	
}
int main()
{
	scanf("%s",s+1);n=strlen(s+1);scanf("%d",&m);int now=1,cnt=0;
	for(int i=1;i<=n;i++)
	{
		if('a'<=s[i]&&s[i]<='z')
		{
			int v=s[i]-'a';
			if(!tr[now][v]) tr[now][v]=++tot,fa[tot]=now;
			now=tr[now][v];
		} 
		else if(s[i]=='P') id[++cnt]=now;
		else now=fa[now];
	}
	memcpy(cpy,tr,sizeof(tr));build_AC();for(int i=2;i<=tot;i++) add(fail[i],i);dfs(1);
	for(int i=1;i<=m;i++) scanf("%d%d", &q[i].x,&q[i].y),ask[id[q[i].y]].push_back(i);solve(1);
	for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
	return 0;
}

标签:AC,P2414,int,离线,fail,字符串,include,节点
From: https://www.cnblogs.com/ListenSnow/p/17216144.html

相关文章

  • MediaCodec 低延时解码
    介绍我们在使用Android的硬解进行解码时,如果是Android11以上则可以使用其特性低延迟,谷歌官方文档以下是Android11支持的低时延特性:ANGLE支持:Android11引入了ANGLE(Al......
  • P5200 [USACO19JAN]Sleepy Cow Sorting G 题解
    前言:教练要求写的,于是过来补发题解。题目传送门分析容易发现后缀如果是上升的那么就不用动,让前面的通过移动插进来就可以了,第一个答案就是\(n\)减去最长上升后缀的长......
  • MediaCodec硬解流程
    一MediaCodec概述MediaCodec是Android4.1(api16)版本引入的低层编解码接口,同时支持音视频的编码和解码。通常与MediaExtractor``、MediaMuxer、AudioTrack结合使用,能够......
  • AcWing100 -- 差分 & 贪心
    1.题目描述题目给定我们一个数组,我们每次可以对数组的一段区间加一或者减一,问我们,使得序列所有数字相等的最少操作次数以及方案个数2.思路很容易想到差分,将题目转......
  • 机器学习日志 泰坦尼克飞船 Spaceship Titanic
    PassengerId——乘客编号。每个编号的形式都表示乘客与是否是组团旅行有关,比如家庭出游,集体出差等,因此编号中有部分是表示他们在团队中的号码。但有部分乘客是独自旅行。H......
  • css 背景 background
    属性值说明CSSbackground-color指定要使用的背景颜色1background-position指定背景图像的位置1background-size指定背景图片的大小3background-......
  • AcWing3305 -- 建图
    1.题目描述给定我们一些初始作物,和作物之间杂交的规则(作物\(a\)和作物\(b\)杂交产生种子\(c\),花费作物\(a\)和作物\(b\)成熟时间的最大值),让我们求,某个作物\(T......
  • nacos报错 Caused by: com.alibaba.nacos.api.exception.NacosException: java.io.IOE
    麻麻劈,根据这个报错一顿ulimit -n 修改打开文件数,鸡儿报错一直在。 最终修改 vi/etc/sysctl.conf增加三项:fs.inotify.max_queued_events=32768fs.inotify.ma......
  • Paper Reading: Neural random subspace
    目录研究动机文章贡献神经随机子空间算法随机子空间方法NRS架构ExpansionAggregationCNN实现架构总结实验分析数据集ML数据集的结果超参数设置消融实验文档检索数据集计......
  • ChIP-seq | ATAC-seq | Cut&Run | 新手指南
     没想到我是先玩了Cut&Run和单细胞ATAC-seq,搞通了再来分析ChIP-seq|ATAC-seq,因为之前接触到的数据太烂了,所以什么有意义的东西都没搞出来,又没有重复,导致分析和评估无从......