AmazingCounters.com

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

Time Limit: 10 Sec  Memory Limit: 256 MB

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

Solution

  题目相当于给出一棵Trie树,每次询问Trie树上两个节点,其中一个节点表示的字符串在另一个节点表示的字符串中出现多少次。这个过程相当于KMP,自然想到对这棵Trie树构建AC自动机,现在假设我们很暴力地拿第二个串丢进AC自动机中匹配,每次走到一个节点,就走一遍这个点的fail指针看看是否匹配到第一个串,由于第二个串原来就在Trie树里,我们在AC自动机上走的时候可以很顺利地顺着Trie树走而不用走fail,于是问题又变成了每次询问Trie树上一个节点(第二个串)到根的路径上有多少个点在fail树上在另一个节点(第一个串)的子树内,把询问离线,对fail树求dfs序,然后对Trie树做dfs,每次往以fail树dfs序建立的线段树/树状数组中加点删点并处理询问即可。总复杂度O(nlogn)。

Code

#include<cstdio>
#include<vector>
using namespace std;
inline int read()
{
    int x;char c;
    while((c=getchar())<'0'||c>'9');
    for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0';
    return x;
}
#define MN 100000
struct edge{int nx,t;}e[MN+5];
int c[MN+5][26],fa[MN+5],tn,l[MN+5],r[MN+5],dfn,a[MN+5],cnt,ans[MN+5];
int f[MN+5],q[MN+5],qn,h[MN+5],en,b[MN+5];
char s[MN+5];
vector<pair<int,int> > v[MN+5];
inline void ins(int x,int y){e[++en]=(edge){h[x],y};h[x]=en;}
void pre(int x)
{
    l[x]=++dfn;
    for(int i=h[x];i;i=e[i].nx)pre(e[i].t);
    r[x]=dfn;
}
void build()
{
    int i,j,k;
    for(i=0;i<26;++i)if(c[0][i])ins(0,q[++qn]=c[0][i]);
    for(i=1;i<=qn;++i)for(j=0;j<26;++j)if(c[q[i]][j])
    {
        for(k=f[q[i]];k&&!c[k][j];)k=f[k];
        ins(f[c[q[i]][j]]=c[k][j],q[++qn]=c[q[i]][j]);
    }
}
void add(int k,int x){for(;k<=MN;k+=k&-k)b[k]+=x;}
int sum(int k){int r=0;for(;k;k-=k&-k)r+=b[k];return r;}
void dfs(int x)
{
    add(l[x],1);
    for(int i=0;i<v[x].size();++i)
    {
        int t=v[x][i].second;
        ans[v[x][i].first]=sum(r[t])-sum(l[t]-1);
    }
    for(int i=0;i<26;++i)if(c[x][i])dfs(c[x][i]);
    add(l[x],-1);
}
int main()
{
    int i,p,x,y;
    scanf("%s",s);
    for(i=p=0;s[i];++i)
        if(s[i]=='P')a[++cnt]=p;
        else if(s[i]=='B')p=fa[p];
        else s[i]-='a',c[p][s[i]]?0:fa[c[p][s[i]]=++tn]=p,p=c[p][s[i]];
    build();pre(0);
    for(p=read(),i=1;i<=p;++i)
    {
        x=read();y=read();
        v[a[y]].push_back(make_pair(i,a[x]));
    }
    dfs(0);
    for(i=1;i<=p;++i)printf("%d\n",ans[i]);
}
posted on 2017-04-11 19:47  ditoly  阅读(290)  评论(0编辑  收藏  举报