• BZOJ3881 Coci2015 Divljak fail树+差分


    题目大意,给出两个字符串集合S和T,向T中添加字符串,查询S_i在T中有几个字符串出现过。一看这种多字符串匹配问题,我们联想到了AC自动机,做法就是,对于S集合我们建立一个AC自动机,建出fail树,fail树有一个很好的性质就是,对于一个节点x,它所对应的字符串是它子树中所有节点对应的字符串的后缀。我们考虑如果S_x在P_x 中出现过,他肯定是P_x某一个前缀的后缀,所以我们把P_x在AC自动机上跑,跑到每一个节点我们更新一下他所在的fail树,统计答案的时候只需统计子树的大小就行了。但是这样会有一点小问题,就是会统计重复,如果对于每一个前缀我们都更新一下它到跟的路径和,这样会重复,因为S_x可能会在P_x中出现多次。实际上我们求的是一个点的子树所有链的并集,解决方案很巧妙,利用树上差分,我们按照dfs序排好顺序,然后相邻的两个节点的lca处-1就行了,这样就不会统计重复,利用树状数组维护一下dfs序即可。(有一点需要注意,在trie树上我们一共有tot个节点,那么对于fail树我们有tot+1个节点,树状数组大小注意一下,一开始错在了这里)——by VANE

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2000010;
    char s[N];
    vector<int> g[N];
    int son[N][30],fail[N],L[N],R[N],a[N];
    int sum[N],pos[N],id[N],dep[N];
    int mn[N*2][25],LOG[N*2];
    int tot,n,dfn,cnt;
    int cmp(int x,int y)
    {
        return L[x]<L[y];
    }
    void dfs(int u)
    {
        L[u]=++dfn;
        mn[pos[u]=++cnt][0]=u;
        for(int i=0;i<g[u].size();++i)
        {
            int v=g[u][i];
            dep[v]=dep[u]+1;
            dfs(v);
            mn[++cnt][0]=u;
        }
        R[u]=dfn;
    }
    void insert(int x)
    {
        int l=strlen(s+1);
        int p=0;
        for(int j=1;j<=l;++j)
        {
            if(!son[p][s[j]-'a']) son[p][s[j]-'a']=++tot;
            p=son[p][s[j]-'a'];
        }
        id[x]=p;
    }
    void getfail()
    {
        queue<int> q;
        for(int i=0;i<26;++i)
        if(son[0][i]) q.push(son[0][i]);
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int i=0;i<26;++i)
            if(son[u][i])
            fail[son[u][i]]=son[fail[u]][i],q.push(son[u][i]);
            else son[u][i]=son[fail[u]][i];
        }
    }
    int query(int x)
    {
        int res=0;
        for(;x;x-=x&-x)
        res+=sum[x];
        return res;
    }
    int lca(int x,int y)
    {
        if(pos[x]<pos[y]) swap(x,y);
        int len=pos[x]-pos[y]+1;
        len=LOG[len];
        return min(mn[pos[y]][len],mn[pos[x]-(1<<len)+1][len],cmp);
    }
    void add(int x,int w)
    {
        for(;x<=tot+1;x+=x&-x)
        sum[x]+=w;
    }
    int main()
    {
        scanf("%d",&n);
        for(int i=1;i<=n;++i)
        {
            scanf("%s",s+1);
            insert(i);
        }
        getfail();
        for(int i=1;i<=tot;++i) g[fail[i]].push_back(i);
        dfs(0);
        for(int k=1;(1<<k)<=cnt;++k) LOG[1<<k]=k;
        for(int i=3;i<=cnt;++i)
        if(!LOG[i]) LOG[i]=LOG[i-1];
        for(int k=1;k<=LOG[cnt];++k)
        for(int i=1;i+(1<<k)-1<=cnt;++i)
        mn[i][k]=min(mn[i][k-1],mn[i+(1<<k-1)][k-1],cmp);
        int q;
        scanf("%d",&q);
        while(q--)
        {
            int opt;scanf("%d",&opt);
            if(opt==1)
            {
                scanf("%s",s+1);
                int l=strlen(s+1);
                int p=0;
                for(int i=1;i<=l;++i)
                a[i]=p=son[p][s[i]-'a'];
                sort(a+1,a+1+l,cmp);
                for(int i=1;i<=l;++i)
                add(L[a[i]],1);
                for(int i=1;i<l;++i)
                {
                    int x=lca(a[i],a[i+1]);
                    add(L[x],-1);
                }
            }
            else
            {
                int x;
                scanf("%d",&x);
                printf("%d
    ",query(R[id[x]])-query(L[id[x]]-1));
            }
        }
    }
  • 相关阅读:
    前端构建工具gulpjs的使用介绍及技巧
    mysql /*! 50100 ... */ 条件编译
    linux 硬连接与软连接
    Linux 数据流重定向
    倒排索引
    sed 常用的功能
    linux mysql安装
    mysql help
    linux 命令行选项
    mysql 主主复制的配置流程
  • 原文地址:https://www.cnblogs.com/nbwzyzngyl/p/8331345.html
Copyright © 2020-2023  润新知