2434: [Noi2011]阿狸的打字机 fail树+dfs序+树状数组

来源:互联网 发布:阿里云二级域名注册 编辑:程序博客网 时间:2024/05/21 22:33

一天内做了两道AC自动机两道fail树的题目(好吧本质都一样啊就是fail树的不同应用→ →),感觉对fail树的理解逐渐加深。
对于这道题目,即给你N个字符串,每次给定两个字符串x,y,询问x在y中出现的次数。
一个直接的暴力思路是做M遍KMP,复杂度肯定爆表啦。。也就40分。
我们考虑fail树,如果y中有一个节点i指向了x的尾节点,证明字符串y中以I为结尾的后缀和字符串x中以x的尾节点结尾的前缀相同,而以x的尾节点结尾的前缀正是x串,也就出现了一次。那么知道了这一点,y中如果有ans个节点的fail指针指向x的尾节点,那么答案就是这个ans了。
我们可以构建出这棵fail树,因为每个点的出度为1,如果把边反向,我们就会得到一棵树了。而上文提到的ans就是以x的尾节点为根的子树中y中节点出现的次数,这个画画图就知道了。
于是我再次想到了暴力(我太弱啦QAQ),每次暴力dfs,看起来这样还是可以过70分的数据的。


然后我脑洞就开到了这里。。默默地看了题解。。QAQ
我居然连dfs序都没想到。。。

PoPoQQQ:我们把关于y的询问都存在邻接表里
然后把y所有的节点在DFS序中的位置插入树状数组,然后对于关于y的每个询问在树状数组上查询一遍即可。

感觉好机智啊,直接离线处理。

求出DFS序之后 我们回来考虑这些操作序列

尾添加一个字符->添加的字符所在节点加入树状数组
在结尾删除一个字符->删除的字符所在节点从树状数组删除
打印当前字串-> 处理询问
处理询问

真是一道好题啊,最后1A了赞。

#include<cstdio>#define N 100005#define lowbit(i) (i&(-i))#include<cstring>using namespace std;int len,m,cnt,sum,dfn;int in[N],out[N],tree[N<<1];int head[N],head_[N],next[N],next_[N],list[N],list_[N],pos[N],ans[N],fa[N],p[N],q[N],a[N][26];char s[N];inline int read(){    int a=0,f=1; char c=getchar();    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}    return a*f;}inline void insert(int x,int y){    next[++sum]=head[x];    head[x]=sum;    list[sum]=y;}inline void add(int x,int y,int tot){    next_[tot]=head_[x];    head_[x]=tot;    list_[tot]=y;}inline void build_tree(){    cnt=1;     int x=1,id=0;    for (int i=0;i<26;i++) a[0][i]=1;    for (int i=0;i<len;i++)        switch(s[i])        {            case 'B':                x=fa[x];                break;            case 'P':                pos[++id]=x;                break;            default:                if (!a[x][s[i]-'a']) a[x][s[i]-'a']=++cnt,fa[cnt]=x;                x=a[x][s[i]-'a'];                break;        }}inline void build_fail(){    int t=0,w=1,x;    q[1]=1; p[1]=0;    while (t<w)    {        x=q[++t];        for (int i=0;i<26;i++)            if (a[x][i])            {                int k=p[x];                while (!a[k][i]) k=p[k];                p[a[x][i]]=a[k][i];                q[++w]=a[x][i];            }    }}void dfs(int x){    in[x]=++dfn;    for (int i=head[x];i;i=next[i]) dfs(list[i]);    out[x]=++dfn;}inline void Add(int x,int val){    for (int i=x;i<=dfn;i+=lowbit(i)) tree[i]+=val;}inline int query(int x){    int tmp=0;    for (int i=x;i;i-=lowbit(i)) tmp+=tree[i];    return tmp;}inline void solve(){    int x=1,id=0;    Add(in[1],1);    for (int i=0;i<len;i++)        switch(s[i])        {            case 'P':                for (int j=head_[++id];j;j=next_[j])                {                    int t=pos[list_[j]];                    ans[j]=query(out[t])-query(in[t]-1);                }                break;            case 'B':                Add(in[x],-1);                x=fa[x];                break;            default:                x=a[x][s[i]-'a'];                Add(in[x],1);                break;        }}               int main(){    scanf("%s",s);    len=strlen(s);    build_tree();    build_fail();    for (int i=1;i<=cnt;i++) insert(p[i],i);    m=read();    for (int i=1;i<=m;i++)    {        int u=read(),v=read();        add(v,u,i);    }    dfs(0);    solve();    for (int i=1;i<=m;i++) printf("%d\n",ans[i]);    return 0;}
0 0
原创粉丝点击