一天内做了两道AC自动机两道fail树的题目(好吧本质都一样啊就是fail树的不同应用→ →),感觉对fail树的理解逐渐加深。
对于这道题目,即给你N个字符串,每次给定两个字符串x,y,询问x在y中出现的次数。
一个直接的暴力思路是做M遍KMP,复杂度肯定爆表啦。。也就40分。
我们考虑fail树,如果y中有一个节点i指向了x的尾节点,证明字符串y中以I为结尾的后缀和字符串x中以x的尾节点结尾的前缀相同,而以x的尾节点结尾的前缀正是x串,也就出现了一次。那么知道了这一点,y中如果有ans个节点的fail指针指向x的尾节点,那么答案就是这个ans了。
我们可以构建出这棵fail树,因为每个点的出度为1,如果把边反向,我们就会得到一棵树了。而上文提到的ans就是以x的尾节点为根的子树中y中节点出现的次数,这个画画图就知道了。
于是我再次想到了暴力(我太弱啦QAQ),每次暴力dfs,看起来这样还是可以过70分的数据的。
然后我脑洞就开到了这里。。默默地看了题解。。QAQ
我居然连dfs序都没想到。。。
PoPoQQQ:我们把关于y的询问都存在邻接表里
然后把y所有的节点在DFS序中的位置插入树状数组,然后对于关于y的每个询问在树状数组上查询一遍即可。
感觉好机智啊,直接离线处理。
求出DFS序之后 我们回来考虑这些操作序列
尾添加一个字符->添加的字符所在节点加入树状数组
在结尾删除一个字符->删除的字符所在节点从树状数组删除
打印当前字串-> 处理询问
处理询问
真是一道好题啊,最后1A了赞。
#include<cstdio>
#define N 100005
#define lowbit(i) (i&(-i))
#include<cstring>
using namespace std;
int len,m,cnt,sum,dfn;
int in[N],out[N],tree[N<<1];
int head[N],head_[N],next[N],next_[N],list[N],list_[N],pos[N],ans[N],fa[N],p[N],q[N],a[N][26];
char s[N];
inline int read()
{
int a=0,f=1; char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
return a*f;
}
inline void insert(int x,int y)
{
next[++sum]=head[x];
head[x]=sum;
list[sum]=y;
}
inline void add(int x,int y,int tot)
{
next_[tot]=head_[x];
head_[x]=tot;
list_[tot]=y;
}
inline void build_tree()
{
cnt=1;
int x=1,id=0;
for (int i=0;i<26;i++) a[0][i]=1;
for (int i=0;i<len;i++)
switch(s[i])
{
case 'B':
x=fa[x];
break;
case 'P':
pos[++id]=x;
break;
default:
if (!a[x][s[i]-'a']) a[x][s[i]-'a']=++cnt,fa[cnt]=x;
x=a[x][s[i]-'a'];
break;
}
}
inline void build_fail()
{
int t=0,w=1,x;
q[1]=1; p[1]=0;
while (t<w)
{
x=q[++t];
for (int i=0;i<26;i++)
if (a[x][i])
{
int k=p[x];
while (!a[k][i]) k=p[k];
p[a[x][i]]=a[k][i];
q[++w]=a[x][i];
}
}
}
void dfs(int x)
{
in[x]=++dfn;
for (int i=head[x];i;i=next[i]) dfs(list[i]);
out[x]=++dfn;
}
inline void Add(int x,int val)
{
for (int i=x;i<=dfn;i+=lowbit(i)) tree[i]+=val;
}
inline int query(int x)
{
int tmp=0;
for (int i=x;i;i-=lowbit(i)) tmp+=tree[i];
return tmp;
}
inline void solve()
{
int x=1,id=0;
Add(in[1],1);
for (int i=0;i<len;i++)
switch(s[i])
{
case 'P':
for (int j=head_[++id];j;j=next_[j])
{
int t=pos[list_[j]];
ans[j]=query(out[t])-query(in[t]-1);
}
break;
case 'B':
Add(in[x],-1);
x=fa[x];
break;
default:
x=a[x][s[i]-'a'];
Add(in[x],1);
break;
}
}
int main()
{
scanf("%s",s);
len=strlen(s);
build_tree();
build_fail();
for (int i=1;i<=cnt;i++) insert(p[i],i);
m=read();
for (int i=1;i<=m;i++)
{
int u=read(),v=read();
add(v,u,i);
}
dfs(0);
solve();
for (int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}