【BZOJ2434-[Noi2011]】阿狸的打字机(AC自动机(fail树)+离线+树状数组)

时间:2023-11-25 15:24:38

Description

阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有26个小写英文字母和'B'、'P'两个字母。

经阿狸研究发现,这个打字机是这样工作的:

l 输入小写字母,打字机的一个凹槽中会加入这个字母(这个字母加在凹槽的最后)。

l 按一下印有'B'的按键,打字机凹槽中最后一个字母会消失。

l 按一下印有'P'的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失。

例如,阿狸输入aPaPBbP,纸上被打印的字符如下:

a

aa

ab

我们把纸上打印出来的字符串从1开始顺序编号,一直到n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。

阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?

Input

输入的第一行包含一个字符串,按阿狸的输入顺序给出所有阿狸输入的字符。

第二行包含一个整数m,表示询问个数。

接下来m行描述所有由小键盘输入的询问。其中第i行包含两个整数x, y,表示第i个询问为(x, y)。

Output

输出m行,其中第i行包含一个整数,表示第i个询问的答案。

Sample Input

aPaPBbP

3

1 2

1 3

2 3

Sample Output

2

1

0

HINT

1<=N<=10^5

1<=M<=10^5
输入总长<=10^5

【分析】

  •   这道题是用AC自动机里的fail指针连成的树做的。
  •   用到fail树。
  •   首先有一个朴素算法就是找到第y个单词在trie树上的路径然后沿着每一个点的fail指针走,如果找到x就加1(想想fail指针建立的过程)。
  •   由此可以运用逆向思维,以x为根的子树沿着fail指针倒着走能找到多少个y路径上的点就说明x在y上出现过几次。每次都dfs找一遍,用树状数组维护,这样可以得到70分。
  •   同时这是一个离线算法,一遍dfs,遇到一个结束标记,就做一下这个串的询问,插一个点在树状数组+1,离开这个点时-1。
  •   对于树上每个点只插入一次,时间复杂度就得到了保证。

有一个很详细的题解:http://blog.csdn.net/huzecong/article/details/7769988

代码如下:(丑)

 #include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define Maxn 100100
#define Maxl 100010 char s[Maxl];
int p[Maxn]; struct hp
{
int x,y,next,id,ans;
}qy[Maxn];int al; struct node
{
int son[],cnt,fail;
int num,rt,fa,st;
}t[Maxn];int tot; int first[Maxn],c[Maxn]; bool cmp(hp x,hp y) {return p[x.y]<p[y.y];}
bool cmp2(hp x,hp y) {return x.id<y.id;} void ins(int x,int y)
{
qy[++al].x=x;qy[al].y=y;
qy[al].next=first[x];first[x]=al;
} void upd(int x,int f)
{
t[x].cnt=;t[x].fa=f;t[x].st=;
memset(t[x].son,,sizeof(t[x].son));
} void read_trie()
{
scanf("%s",s+);
int len=strlen(s+);
memset(p,,sizeof(p));
int now=;int ql=;tot=;
for(int j=;j<=len;j++)
{
if(s[j]>='a'&&s[j]<='z')
{
int ind=s[j]-'a'+;
if(!t[now].son[ind])
t[now].son[ind]=++tot,upd(tot,now);
now=t[now].son[ind];
if(j==len) t[now].cnt++;
}
else if(s[j]=='B') now=t[now].fa;
else p[++ql]=now;
}
} queue<int > q;
void build_AC()
{
int i,j,x,y;
while(!q.empty()) q.pop();
q.push();
while(!q.empty())
{
x=q.front();
y=t[x].fail;
for(j=;j<=;j++)
{
if(t[x].son[j])
{
q.push(t[x].son[j]);
t[t[x].son[j]].fail=x?t[y].son[j]:x;
ins(t[t[x].son[j]].fail,t[x].son[j]);
}
else t[x].son[j]=t[y].son[j];
}
q.pop();
}
} void dfs(int x)
{
t[x].num=++al;t[x].rt=t[x].num;
for(int i=first[x];i;i=qy[i].next)
dfs(qy[i].y),t[x].rt=t[qy[i].y].rt;
} void add(int x,int y)
{
for(int i=x;i<=tot+;i+=i&(-i))
c[i]+=y;
} int getsum(int x)
{
int ans=;
for(int i=x;i>=;i-=i&(-i))
ans+=c[i];
return ans;
} void dfs2(int x)
{
add(t[x].num,);
if(t[x].st!=)
{
for(int i=t[x].st;;i++)
{
if(p[qy[i].y]!=x) break;
qy[i].ans=getsum(t[p[qy[i].x]].rt)-getsum(t[p[qy[i].x]].num-);
}
}
for(int i=;i<=;i++) if(t[x].son[i]&&t[t[x].son[i]].fa==x)
{
dfs2(t[x].son[i]);
}
add(t[x].num,-);
} void init()
{
memset(first,,sizeof(first));
al=;
read_trie();
build_AC();al=;
dfs();
int m;
scanf("%d",&m);
for(int i=;i<=m;i++) {scanf("%d%d",&qy[i].x,&qy[i].y);qy[i].id=i;}
sort(qy+,qy++m,cmp);
qy[].y=;
for(int i=;i<=m;i++)
if(p[qy[i].y]!=p[qy[i-].y]) t[p[qy[i].y]].st=i;
else t[p[qy[i].y]].st=t[p[qy[i-].y]].st;
memset(c,,sizeof(c));
dfs2();
sort(qy+,qy++m,cmp2);
for(int i=;i<=m;i++) printf("%d\n",qy[i].ans);
} int main()
{
init();
return ;
}

[BZOJ2434]

2016-06-16 16:55:20