bzoj3881 [Coci2015]Divljak(AC自动机+fail树+dfs序+树状数组+树链剖分)

83 篇文章 0 订阅

bzoj3881 [Coci2015]Divljak

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=3881

题意:
Alice有n个字符串 S1,S2...Sn S 1 , S 2 . . . S n ,Bob有一个字符串集合T,一开始集合是空的。
接下来会发生q个操作,操作有两种形式:
“1 P”,Bob往自己的集合里添加了一个字符串P。
“2 x”,Alice询问Bob,集合T中有多少个字符串包含串 Sx S x 。(我们称串A包含串B,当且仅当B是A的子串)
Bob遇到了困难,需要你的帮助。

数据范围
1 <= n,q <= 100000
Alice和Bob拥有的字符串长度之和各自都不会超过 2000000
字符串都由小写英文字母组成。

题解:
论文里面的题解:
这里写图片描述

好题。想到对S建AC自动机就挺妙的。
A包含串B多少次,就是A在AC自动机上的每个节点,有多少在B结尾节点的fail树子树中。
那么:
   1操作相当于把P的这些节点染上一种新的颜色(颜色不覆盖可并存)
   2操作相当于查询 Sx S x 结尾节点fail树子树颜色种类。
   
就是经典的按dfs序排序,u++,v- -,lca(u,v)- -的操作,每次查询子树权值和,用树状数组维护。

并不需要把P也添加进AC自动机,最后还得对应到S的节点上,直接在S集合构建的AC自动机上面跑,把经过的串操作了即可。

倍增lca会被卡,写树链剖分或LCT。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=2000003;
queue<int> Q;
int n,q,ch[N][26],fail[N],root=1,tail=1,S[N],cnt=0,C[N];
int head[N],to[N],nxt[N],num=0,pos[N],size[N],son[N],top[N],in[N],out[N],inc=0,dep[N],fa[N];
char str[N];
bool cmp(const int &A,const int &B){return in[A]<in[B];}
void ADD(int x,int d) {for(int i=x;i<=inc;i=i+(i&(-i))) C[i]+=d;}
inline int query(int x){int ret=0; for(int i=x;i;i-=(i&(-i))) ret+=C[i]; return ret;}
void build(int u,int v)
{
    num++;
    to[num]=v;
    nxt[num]=head[u];
    head[u]=num;
}
inline int insert()
{
    int len=strlen(str); int tmp=root;
    for(int i=0;i<len;i++)
    {
        int c=str[i]-'a';
        if(!ch[tmp][c]) ch[tmp][c]=++tail;
        tmp=ch[tmp][c];
    }
    return tmp;
}
void getfail()
{
    for(int i=0;i<26;i++) if(ch[root][i]) fail[ch[root][i]]=root,build(root,ch[root][i]),Q.push(ch[root][i]); else ch[root][i]=root;
    while(!Q.empty())
    {
        int top=Q.front(); Q.pop();
        for(int i=0;i<26;i++)
        {
            if(!ch[top][i]) ch[top][i]=ch[fail[top]][i];
            else
            {
                int u=ch[top][i];
                fail[u]=ch[fail[top]][i];
                build(fail[u],u);
                Q.push(u);
            }
        }
    }
}
void dfs1(int u,int f)
{
    size[u]=1;  dep[u]=dep[f]+1; fa[u]=f;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        dfs1(v,u); size[u]+=size[v];
        if(size[v]>size[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int tp)
{
    top[u]=tp; inc++; in[u]=inc;
    if(son[u]) dfs2(son[u],tp);
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(v==son[u]) continue;
        dfs2(v,v);
    }
    out[u]=inc;
}
int getlca(int u,int v)
{
    while(top[u]!=top[v])
    {
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        u=fa[top[u]];
    }
    return dep[u]<dep[v]?u:v;
}
void add()
{
    int len=strlen(str); int tmp=root; cnt=0;
    for(int i=0;i<len;i++)
    {
        int c=str[i]-'a';
        tmp=ch[tmp][c];
        S[++cnt]=tmp;
    }
    sort(S+1,S+cnt+1,cmp); int last=0;
    for(int i=1;i<=cnt;i++)
    {
        if(S[i]==last) continue;
        ADD(in[S[i]],1);
        if(last!=0) ADD(in[getlca(last,S[i])],-1);
        last=S[i];
    }
}
int main()
{
    scanf("%d",&n); 
    for(int i=1;i<=n;i++)
    {
        scanf("%s",str);
        pos[i]=insert();
    }
    getfail();
    dfs1(1,1); dfs2(1,1);
    scanf("%d",&q);
    while(q--)
    {
        int opt,x;
        scanf("%d",&opt);
        if(opt==1) scanf("%s",str),add();
        else
        {
            scanf("%d",&x);
            printf("%d\n",query(out[pos[x]])-query(in[pos[x]]-1));
        }
    }
    return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值