[JZOJ2784][BZOJ2434]【NOI2011】阿狸的打字机

来源:互联网 发布:php画三角形星星 编辑:程序博客网 时间:2024/05/18 01:46

Description

阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机。打字机上只有28个按键,分别印有 26个小写英文字母和’B’、’P’两个字母。 经阿狸研究发现,这个打字机是这样工作的:
输入小写字母,打字机的一个凹槽中会加入这个字母(按 P 前凹槽中至少有一个字母)。
按一下印有’B’的按键,打字机凹槽中最后一个字母会消失。
按一下印有’P’的按键,打字机会在纸上打印出凹槽中现有的所有字母并换行,但凹槽中的字母不会消失(保证凹槽中至少有一个字母) 。
例如,阿狸输入 aPaPBbP,纸上被打印的字符如下:
a
aa
ab
我们把纸上打印出来的字符串从 1开始顺序编号,一直到 n。打字机有一个非常有趣的功能,在打字机中暗藏一个带数字的小键盘,在小键盘上输入两个数(x,y)(其中1≤x,y≤n),打字机会显示第x个打印的字符串在第y个打印的字符串中出现了多少次。 阿狸发现了这个功能以后很兴奋,他想写个程序完成同样的功能,你能帮助他么?
1≤n≤ 10^5
1≤m≤ 10^5
输入文件第一行的字符数≤10^5

Solution

先将AC自动机构出来,同时构出fail树

可以发现,一个点所代表的串,一定都在这个点在fail树上的子树内的所有点所代表的字符串出现过。

那么对于一个询问(x,y)只需要统计x在fail树上的子树中包含了y的路径上的多少个点

可以把所有询问挂在它们的y上
子树统计可以用树状数组维护fail树的DFS序

那么构完以后重新扫一遍字符串,相应的在Trie上跑,遇到P就统计,遇到B就在树桩树组中删掉对应的,并且跳到它在Trie上的父亲,遇到字母就相应加入树状数组

复杂度O(Len+m)

Code

#include <cstdio>#include <cstdlib>#include <iostream>#include <algorithm>#include <cstring>#include <cmath>#include <vector>#define N 200005#define fo(i,a,b) for(int i=a;i<=b;i++)#define fod(i,a,b) for(int i=a;i>=b;i--)using namespace std;int n,n1,m1,fail[N],fs[N],nt[N],dt[N],m,t[N][26],c[N],pt[N],fa[N],lx[N],rx[N],d[N],vl[N],dfn[N],df[N],le[N],qs[N][2],ans[N];vector<int> ask[N];char ch[N];void link(int x,int y){    nt[++m1]=fs[x];    dt[fs[x]=m1]=y;}void dfs(int k){    dfn[++dfn[0]]=k,lx[k]=dfn[0],df[k]=dfn[0];    for(int i=fs[k];i;i=nt[i]) dfs(dt[i]);    rx[k]=dfn[0];}void prp(){    int l=0,r=1;    d[1]=1,fail[1]=0;    while(l<r)    {        int k=d[++l];        fo(i,0,25)        {            int p=t[k][i];            if(p>0)            {                d[++r]=p;                if(k==1) fail[p]=1,link(1,p);                else                {                    fail[p]=fail[k];                    while(fail[p]>1&&t[fail[p]][vl[p]]==0) fail[p]=fail[fail[p]];                    if(t[fail[p]][vl[p]]) fail[p]=t[fail[p]][vl[p]];                    link(fail[p],p);                }            }        }    }}int lowbit(int k){    return (k&(-k));}int get(int k){    int s=0;    while(k) s+=c[k],k-=lowbit(k);    return s;}void put(int k,int v){    while(k<=n1) c[k]+=v,k+=lowbit(k);}int main(){    scanf("%s",ch+1);    n=strlen(ch+1);    n1=1;    int k=1;    fo(i,1,n)     {        if(ch[i]=='B') k=fa[k];        else if(ch[i]=='P') pt[++m]=k;        else         {            if(!t[k][ch[i]-'a']) t[k][ch[i]-'a']=++n1,fa[n1]=k;            k=t[k][ch[i]-'a'];            vl[k]=ch[i]-'a';        }    }    prp();    dfs(1);    int q;    cin>>q;    fo(i,1,q)    {        scanf("%d%d",&qs[i][0],&qs[i][1]);        ask[pt[qs[i][1]]].push_back(i),le[pt[qs[i][1]]]++;    }    k=1;    fo(i,1,n)    {        if(ch[i]=='B') put(df[k],-1),k=fa[k];        else if(ch[i]=='P')         {            fo(j,0,le[k]-1)            {                int x=ask[k][j];                ans[x]=get(rx[pt[qs[x][0]]])-get(lx[pt[qs[x][0]]]-1);            }        }        else         {            k=t[k][ch[i]-'a'];            put(df[k],1);        }    }    fo(i,1,q) printf("%d\n",ans[i]);}
原创粉丝点击