题目大意:初始字串为空,首先给定一系列操作序列,有三种操作:
1.在结尾加一个字符
2.在结尾删除一个字符
3.打印当前字串
然后多次询问第x个打印的字串在第y个打印的字串中出现了几次
卡了很久……到底还是对AC自动机了解不是很深啊QAQ
fail树不是很难想 至少在用AC自动机切掉3172之后不是很难想……
首先构建AC自动机 注意由于这个字串的特殊构造 我们不必每打印一个字符串再插入Trie树
令now为当前指针 初始为root 考虑三种操作
在结尾添加一个字符->新建一个子节点(若存在在不用新建),进入该子节点
在结尾删除一个字符->返回到父亲节点
打印当前字串->在当前节点标记是第几个字符串
那么查询x在y中出现了几次 就是查询y有多少个节点沿着fail指针能找到x (AC自动机基本操作)
那么我们反向思考 查询y有多少个节点沿着fail指针能找到x 就是查询x沿着反向的fail指针能找到多少个y的节点
fail指针没有环 每个节点只有一个出度 那么反向之后显然是一棵树 x沿着反向的fail指针所能到达的节点就是x所在的子树
于是我们可以沿着反向的fail指针搞出DFS序 x所在的子树就是DFS中对应的区间 我们要查询的是x对应的区间中有多少个y
想高级数据结构的可以洗洗睡了
我们可以这么搞
对于每个y:
我们把关于y的询问都存在邻接表里 然后把y所有的节点在DFS序中的位置插入树状数组,然后对于关于y的每个询问在树状数组上查询一遍即可。
那么问题来了:这不会TLE么?
答案是不会的 因为这题的特殊性 所以树状数组可以达到O(nlogn)
求出DFS序之后 我们回来考虑这些操作序列
在结尾添加一个字符->添加的字符所在节点加入树状数组
在结尾删除一个字符->删除的字符所在节点从树状数组删除
打印当前字串-> 处理询问
然后就搞出来了 1A我也是醉了
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
using namespace std;
struct Trie{
Trie *son[26],*fail;
vector<Trie*> next;
int ed,pos;
void* operator new (size_t);
}*root=new Trie,mempool[M],*C=mempool;
struct abcd{
int to,pos,next;
}table[M];
int head[M],tot;
int n,m,ans[M];
char s[M];
int start[M],end[M],cnt;
int c[M];
void* Trie :: operator new (size_t)
{
return C++;
}
void Add(int x,int y,int z)
{
table[++tot].to=y;
table[tot].pos=z;
table[tot].next=head[x];
head[x]=tot;
}
void Update(int x,int y)
{
for(;x<=cnt;x+=x&-x)
c[x]+=y;
}
int Get_Ans(int x)
{
int re=0;
for(;x;x-=x&-x)
re+=c[x];
return re;
}
void Build_Tree()
{
int i;
static Trie* stack[M];
static int top;
stack[++top]=root;
for(i=1;s[i];i++)
{
switch(s[i])
{
case 'B':
stack[top--]=0x0;
break;
case 'P':
stack[top]->ed=++n;
break;
default:
if(!stack[top]->son[s[i]-'a'])
stack[top]->son[s[i]-'a']=new Trie;
stack[++top]=stack[top-1]->son[s[i]-'a'];
break;
}
}
static Trie *q[M];
static int r,h;
for(i=0;i<26;i++)
if(root->son[i])
{
root->son[i]->fail=root;
root->next.push_back(q[++r]=root->son[i]);
}
while(r!=h)
{
Trie *p=q[++h];
for(i=0;i<26;i++)
if(p->son[i])
{
Trie *temp=p->fail;
while(temp!=root&&!temp->son[i])
temp=temp->fail;
if(temp->son[i])
temp=temp->son[i];
p->son[i]->fail=temp;
temp->next.push_back(q[++r]=p->son[i]);
}
}
}
void DFS(Trie *p)
{
vector<Trie*>::iterator it;
p->pos=++cnt;
if(p->ed) start[p->ed]=cnt;
for(it=p->next.begin();it!=p->next.end();it++)
DFS(*it);
if(p->ed) end[p->ed]=cnt;
}
void Solve()
{
int i,j;
static Trie* stack[M];
static int top;
stack[++top]=root;
for(j=1;s[j];j++)
{
switch(s[j])
{
case 'B':
Update(stack[top]->pos,-1);
stack[top--]=0x0;
break;
case 'P':
for(i=head[stack[top]->ed];i;i=table[i].next)
ans[table[i].pos]=Get_Ans(end[table[i].to])-Get_Ans(start[table[i].to]-1);
break;
default:
stack[++top]=stack[top-1]->son[s[j]-'a'];
Update(stack[top]->pos,1);
break;
}
}
}
int main()
{
int i,x,y;
scanf("%s",s+1);
Build_Tree();
DFS(root);
cin>>m;
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
Add(y,x,i);
}
Solve();
for(i=1;i<=m;i++)
printf("%d\n",ans[i]);
}