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

来源:互联网 发布:淘宝卖阿迪达斯的店铺 编辑:程序博客网 时间:2024/06/04 19:28

题目概述

有一台打字机,可以:

  1. 在字符串末尾插入一个小写字母。
  2. 删除字符串末尾的一个小写字母。
  3. 输出当前字符串。

还有 m 个询问,每个询问 x,y 表示求第 x 个输出的字符串在第 y 个输出的字符串中的出现次数。

解题报告

观察打字的过程,我们发现这其实就是在构造一棵Trie:

  1. 在字符串末尾插入一个小写字母 在当前节点 now 扩展一个字符。
  2. 删除字符串末尾的一个小写字母 now 回退到父亲节点。

先按照打字顺序处理出Trie,然后构造好AC自动机和 fail 树,那么一个询问 x,y 就是查找字符串 x 对应的节点沿着 fail 树向下走能走到多少字符串 y 经过的节点,即查找 x 子树中有多少 y 经过的节点。

所以我们可以用DFS序+树状数组来快速统计 x 的子树,但问题是 y 经过的节点是哪些,如果直接从 fail 树上跳,复杂度肯定不对。由于这道题的构造方法比较特殊,我们会发现,如果想要构造到 yy 经过的节点在之前就肯定出现过了!

既然如此我们考虑离线,先把所有与 y 相关的 x 存下来,按照题目给出的打字顺序再处理一遍:

  1. 在字符串末尾插入一个小写字母 在当前节点 now 扩展一个字符,将新的 now 节点标记为 1
  2. 删除字符串末尾的一个小写字母 now 节点标记为 0 ,并回退到父亲节点。
  3. 输出当前字符串 处理 now 节点对应字符串 y 的所有 x ,答案为 x 子树的权值和。

示例程序

代码有点乱……读者老爷凑合看看吧QAQ。

#include<cstdio>using namespace std;const int maxn=100000,maxm=100000,maxi=26;int n,te,ans[maxm+5];char s[maxn+5];int E,lnk[maxn+5],qlnk[maxn+5],nxt[maxn+maxm+5],chd[maxn+maxm+5],w[maxn+maxm+5];void Add(int x,int y) {chd[++E]=y;nxt[E]=lnk[x];lnk[x]=E;}void Add(int x,int y,int z) {chd[++E]=y;w[E]=z;nxt[E]=qlnk[x];qlnk[x]=E;}int si=1,son[maxn+5][maxi],top,stk[maxn+5],ID[maxn+5];int que[maxn+5],fai[maxn+5];void make_AC(){    top=0;    for (int i=1;s[i];i++)        if (s[i]=='B') top--; else        if (s[i]=='P') ID[stk[top]]=++n; else        {            int &u=son[stk[top]][s[i]-'a'];if (!u) u=si++;            stk[++top]=u;        }    int Head=0,Tail=0;    for (int i=0;i<maxi;i++) if (son[0][i])        que[++Tail]=son[0][i],fai[son[0][i]]=0;    while (Head!=Tail)    {        int x=que[++Head];        for (int i=0;i<maxi;i++)        {            if (!son[x][i]) {son[x][i]=son[fai[x]][i];continue;}            int u=son[x][i];que[++Tail]=u;fai[u]=son[fai[x]][i];        }    }    for (int i=1;i<si;i++) Add(fai[i],i);}int ti,Lt[maxn+5],Rt[maxn+5],L[maxn+5],R[maxn+5];void Dfs(int x){    Lt[x]=++ti;if (ID[x]) L[ID[x]]=ti;    for (int j=lnk[x];j;j=nxt[j]) Dfs(chd[j]);    Rt[x]=ti;if (ID[x]) R[ID[x]]=ti;}int c[maxn+5];void Update(int x,int tem) {for (int p=x;p<=ti;p+=p&-p) c[p]+=tem;}int Sum(int x) {int sum=0;for (int p=x;p;p-=p&-p) sum+=c[p];return sum;}void Solve(){    top=0;    for (int i=1;s[i];i++)        if (s[i]=='B') Update(Lt[stk[top--]],-1); else        if (s[i]=='P')        {            for (int j=qlnk[ID[stk[top]]];j;j=nxt[j])                ans[w[j]]=Sum(R[chd[j]])-Sum(L[chd[j]]-1);        } else        {            top++;stk[top]=son[stk[top-1]][s[i]-'a'];            Update(Lt[stk[top]],1);        }}int main(){    freopen("program.in","r",stdin);    freopen("program.out","w",stdout);    scanf("%s%d",s+1,&te);make_AC();    for (int i=1;i<=te;i++)    {        int x,y;scanf("%d%d",&x,&y);        Add(y,x,i);    }    Dfs(0);Solve();    for (int i=1;i<=te;i++) printf("%d\n",ans[i]);    return 0;}
阅读全文
0 0
原创粉丝点击