BZOJ 2434: [Noi2011]阿狸的打字机
很早以前就想写这题了,但一直鸽到今天,不过对AC自动机的理解更加到位了的说
首先我们把原串的AC自动机建出来,由于这里的删除是回退操作,因此我们记录一下每个点的父亲,遇到B
就把指针移到父亲节点即可
考虑AC自动机的性质:
- 在Trie树上一个点的祖先所代表的单词是当前这个点所代表的单词的前缀
- 一个节点的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;
}
辣鸡老年选手AFO在即