BZOJ 2434: [Noi2011]阿狸的打字机

很早以前就想写这题了,但一直鸽到今天,不过对AC自动机的理解更加到位了的说

首先我们把原串的AC自动机建出来,由于这里的删除是回退操作,因此我们记录一下每个点的父亲,遇到B就把指针移到父亲节点即可

考虑AC自动机的性质:

  1. 在Trie树上一个点的祖先所代表的单词是当前这个点所代表的单词的前缀
  2. 一个节点的fail指针指向的字符串是当前这个点所代表的单词的最长的后缀

然后众所周知前缀的后缀就是字串,因此我们可以得出一个暴力做法:

对于\(y\)字符串所代表的每个点,在fail树上暴力向上跳,如果遇到\(x\)字符串的结尾就增加答案

然后我们冷静分析一下,暴力向上肯定不好处理,假设我们把所有\(y\)字符串的点都打上标记,然后看看\(x\)字符串的子树里有多少个标记点也是可行的

那么再进一步,问题变成怎样每次只把\(y\)字符串里的点打好标记呢?很简单,只要离线一下就好了

考虑我们重新模拟建立Trie树的过程,这样我们肯定是在按序遍历所有的字符串。同时我们将询问按\(y\)排序,每次处理所有\(y\)相同的询问

然后每次向下走的时候就把当前点点权\(+1\),向上走就\(-1\)撤销,然后相当于维护\(x\)的子树内的权值和

直接DFS序+树状数组即可,总复杂度\(O(26n+n\log n)\)

#include<cstdio>
#include<cstring>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
struct ques
{
	int x,y,id;
	friend inline bool operator < (const ques& A,const ques& B)
	{
		return A.y<B.y;
	}
}q[N]; int n,m,tot,cur,pos[N],ans[N],fa[N];
struct edge
{
	int to,nxt;
}e[N]; int head[N],cnt,L[N],R[N],idx; char s[N];
inline void addedge(CI x,CI y)
{
	e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
}
class AC_Automation
{
	private:
		int q[N];
	public:
		struct ac_node
		{
			int ch[26],fail;
		}node[N];
		#define next(x,y) node[x].ch[y]
		#define fail(x) node[x].fail
		inline void insert(char *s)
		{
			int ct=0,now=0; for (RI i=1;i<=n;++i) switch (s[i])
			{
				case 'P':
					pos[++ct]=now; break;
				case 'B':
					now=fa[now]; break;
				default:
					if (!next(now,s[i]-'a')) fa[next(now,s[i]-'a')=++tot]=now;
					now=next(now,s[i]-'a'); break;
			}
			
		}
		inline void get_fail(void)
		{
			RI H=0,T=0,i; for (i=0;i<26;++i) if (next(0,i)) addedge(0,q[++T]=next(0,i));
			while (H<T)
			{
				int now=q[++H],to; for (i=0;i<26;++i)
				if (!(to=next(now,i))) next(now,i)=next(fail(now),i); else
				fail(q[++T]=to)=next(fail(now),i),addedge(fail(to),to);
			}
		}
}AC;
class Tree_Array
{
	private:
		int bit[N];
	public:
		#define lowbit(x) (x&-x)
		inline void add(RI x,CI y)
		{
			for (;x<=idx;x+=lowbit(x)) bit[x]+=y;
		}
		inline int get(RI x,int ret=0)
		{
			for (;x;x-=lowbit(x)) ret+=bit[x]; return ret;
		}
		#undef lowbit
}BIT;
inline void DFS(CI now=0)
{
	L[now]=++idx; for (RI i=head[now];i;i=e[i].nxt) DFS(e[i].to); R[now]=idx;
}
int main()
{
	RI i,p=1,now=0; scanf("%s",s+1); n=strlen(s+1); AC.insert(s); AC.get_fail();
	for (scanf("%d",&m),i=1;i<=m;++i) scanf("%d%d",&q[i].x,&q[i].y),q[i].id=i;
	for (sort(q+1,q+m+1),DFS(),i=1;i<=n;++i) switch (s[i])
	{
		case 'P':
			++cur; while (q[p].y<cur&&p<=m) ++p;
			while (q[p].y==cur&&p<=m)
			ans[q[p].id]=BIT.get(R[pos[q[p].x]])-BIT.get(L[pos[q[p].x]]-1),++p; break;
		case 'B':
			BIT.add(L[now],-1); now=fa[now]; break;
		default:
			BIT.add(L[now=AC.node[now].ch[s[i]-'a']],1); break;
	}
	for (i=1;i<=m;++i) printf("%d\n",ans[i]); return 0;
}
posted @ 2020-02-01 21:38  空気力学の詩  阅读(110)  评论(0编辑  收藏  举报