【BZOJ2434】【NOI2011】阿狸的打字机 AC自动机

转载请注明出处233:http://blog.csdn.net/vmurder/article/details/42875307

这是一道神题。

首先我们需要建立AC自动机,然后再建个Fail树,之后发现

如果询问a串在b串中出现了几次,那么只需要看b串在AC自动机上所有的节点中有多少个节点,在a串的结束节点在Fail树上的子树中就可以了。

然后这样做就很可以了,但是仍然不能AC,

这时我们只需要按照Fail树的dfs序建立数据结构(我写了树状数组)进行区间查询就好了。

这时对于以上的b串,我们按照dfs序扫一遍(按照输入时的字符串就是天然的优越dfs序),然后每转移一个节点,就在数据结构上加加减减、、

然后对于每个‘B’,挂链询问中问了哪些‘A’在其中出现了多少次,树状数组O(logn)查一次就好了。

单点修改区间查询,时间复杂度mlogn。

代码:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 101000
#define MAX 220000
#define T 26
using namespace std;
struct FAIL
{
	int v[N],next[N],head[N],cnt;
	void cls()
	{
		cnt=0;
		memset(head,0,sizeof head);
	}
	void add(int u,int _v)
	{
		v[++cnt]=_v;
		next[cnt]=head[u];
		head[u]=cnt;
	}
	int in[N],out[N],dfn;
	void build_dfn(int x=0)
	{
		in[x]=++dfn;
		for(int i=head[x];i;i=next[i])
			build_dfn(v[i]);
		out[x]=++dfn;
	}
}Fail;

struct FENWICK //树状数组
{
	int fenwick[MAX];
	inline void add (int x,int w)
	{
		for(x=Fail.in[x];x<MAX;x+=(x&-x))
			fenwick[x]+=w;
	}
	inline int query(int x)
	{
		int ans=0,temp=x;
		for(x=Fail.out[temp] ;x;x-=(x&-x))ans+=fenwick[x];
		for(x=Fail.in[temp]-1;x;x-=(x&-x))ans-=fenwick[x];
		return ans;
	}
}fw;

struct TRIE
{
	int pa[N],next[N][T],fail[N];
	int end[N],crs[N];
	int root,cnt,id;
	char s[N];
	void build_trie() // 建立Trie
	{
		scanf("%s",s);
		int i,x=root=0,alp;
		for(i=0;s[i];i++)
		{
			if(s[i]=='P')end[x]=++id,crs[id]=x;
			else if(s[i]=='B')x=pa[x];
			else {
				alp=s[i]-'a';
				if(!next[x][alp])next[x][alp]=++cnt,pa[cnt]=x;
				x=next[x][alp];
			}
		}
	}
	void build_fail() // 建立AC自动机
	{
		queue<int>q;
		q.push(root);
		int i,u,v,temp;
		while(!q.empty())
		{
			u=q.front(),q.pop();
			for(i=0;i<T;i++)if(v=next[u][i])
			{
				if(u==root)fail[v]=root;
				else {
					temp=fail[u];
					while(temp&&!next[temp][i])temp=fail[temp];
					fail[v]=next[temp][i];
				}
				q.push(v);
				Fail.add(fail[v],v);
			}
		}
	}
	int v[N],nxt[N],head[N];
	void add(int u,int _v) // 询问离线
	{
		v[++cnt]=_v;
		nxt[cnt]=head[u];
		head[u]=cnt;
	}
	int m,fians[N];
	void input() // 输入询问
	{
		int a,b;
		scanf("%d",&m);
		cnt=0;
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&a,&b);
			add(b,a);
		}
	}
	void query(int pdd)
	{
		for(int i=head[pdd];i;i=nxt[i])
			fians[i]=fw.query(crs[v[i]]);
	}
	void work() // 遍历B串
	{
		int i,x=root=0,alp,now;
		for(i=0;s[i];i++)
		{
			if(s[i]=='P')query(end[x]);
			else if(s[i]=='B')fw.add(x,-1),x=pa[x];
			else {
				alp=s[i]-'a';
				x=next[x][alp];
				fw.add(x,1);
			}
		}
		return ;
	}
	void output() // 输出答案
	{for(int i=1;i<=m;i++)printf("%d\n",fians[i]);}
}Trie;
int main()
{
//	freopen("my.in","r",stdin);
//	freopen("my.out","w",stdout);
	Trie.build_trie();
	Trie.build_fail();
	Fail.build_dfn();
	Trie.input();
	Trie.work();
	Trie.output();
	return 0;
}
时间: 2024-11-05 22:03:39

【BZOJ2434】【NOI2011】阿狸的打字机 AC自动机的相关文章

[BZOJ2434]NOI2011阿狸的打字机|AC自动机|fail树|树状数组

这题真是太神了,好多实用的技巧..首先肯定是要先把每个要输出的串当模式串把自动机给建出来的,如果一个一个串复制出来再一个个插入显然非常慢...我们用在自动机上插入模式串的方法来建,初始时在0,新加一个字符就想下爬(或者新建),维护一个父亲指针,删除的时候就可以爬上去,这样就可以O(n)建出来了.. 再考虑询问的问题,每次把串拿出来再放进自动机跑一遍显然太慢..这里需要用到一个叫做fail树的东西,就是把fail指针当做边建成的一颗树..比如fail(i)=j,那么i在fail树上的父节点就是j.

BZOJ2434 NOI2011 阿狸的打字机 AC自动机+树状数组+DFS序

题意:给定三个操作:1.在当前字符串的末尾添加一个字符c  2.在当前字符串的末尾删除一个字符  3.记录当前字符串并对其标号.再给出N组询问,每组询问需回答第x个字符串在第y个字符串中出现的次数 题解: 首先按照如下规则建Trie,设当前节点为t,第i个字符串的结尾在Trie中的位置为mark[i]: 1.插入操作:看t是否有c这个儿子,有则t=t->child[c],否则t->child[c]=NewNode,t=t->child[c] 2.删除操作:t=t->father 3

BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]

2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2545  Solved: 1419[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的:l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最

BZOJ 2434: [Noi2011]阿狸的打字机( AC自动机 + DFS序 + 树状数组 )

一个串a在b中出现, 那么a是b的某些前缀的后缀, 所以搞出AC自动机, 按fail反向建树, 然后查询(x, y)就是y的子树中有多少是x的前缀. 离线, 对AC自动机DFS一遍, 用dfs序+树状数组维护, DFS到的查询点就回答询问.时间复杂度O(|ACAM|+QlogQ) ------------------------------------------------------------------------------------------- #include<cstdio>

[NOI2011]阿狸的打字机 AC自动机+DFS序+树状数组

[NOI2011]阿狸的打字机 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后).l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失.l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失.例如,阿狸输入aPaPBbP

【BZOJ-2434】阿狸的打字机 AC自动机 + Fail树 + DFS序 + 树状数组

2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 2022  Solved: 1158[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母.经阿狸研究发现,这个打字机是这样工作的:l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最

【BZOJ】2434: [Noi2011]阿狸的打字机 AC自动机+树状数组+DFS序

[题意]阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后). l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失. l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失. 我们把纸上打印出来的字符串从1开始顺序编号,一直到n.打字机有一个非

bzoj 2434 [Noi2011]阿狸的打字机——AC自动机

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2434 dfs AC自动机,走过的点权值+1,回溯的时候权值-1:走到询问的 y 串的节点,看一下此时 x 串 fail 树子树和即可. #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int rdn() { int ret=0;bool fx=1;char ch

【bzoj2434】阿狸的打字机-AC自动机+fail树+优化

http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=23083 Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后). l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失. l 按一下印有'P'的

bzoj 2434 [Noi2011]阿狸的打字机 ac

[Noi2011]阿狸的打字机 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 4001  Solved: 2198[Submit][Status][Discuss] Description 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机.打字机上只有28个按键,分别印有26个小写英文字母和'B'.'P'两个字母. 经阿狸研究发现,这个打字机是这样工作的: l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后).