zoukankan      html  css  js  c++  java
  • 【BZOJ-3881】Divljak AC自动机fail树 + 树链剖分+ 树状数组 + DFS序

    3881: [Coci2015]Divljak

    Time Limit: 20 Sec  Memory Limit: 768 MB
    Submit: 508  Solved: 158
    [Submit][Status][Discuss]

    Description

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

    Input

    第1行,一个数n;
    接下来n行,每行一个字符串表示S_i;
    下一行,一个数q;
    接下来q行,每行一个操作,格式见题目描述。

    Output

    对于每一个Alice的询问,帮Bob输出答案。

    Sample Input

    3
    a
    bc
    abc
    5
    1 abca
    2 1
    1 bca
    2 2
    2 3

    Sample Output

    1
    2
    1

    HINT

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

    Source

    鸣谢 Dzy

    Solution

    首先这个题得利用fail树的性质....

    这种给出一个串在给出的一坨串中出现次数或者是否出现过,这样显然是利用fail树...

    然后就是把路径上的点变成1,询问有多少个1...这个可以利用树状数组维护DFS序得到...

    然后这个题就是先把Alice的串建AC自动机并且建出fail树...

    然后每得到Bob的串,就在fail树上相应的点上+1,但是直接+1会有重复,所以先对所有的点排序,然后直接将+1标记打在节点上,并对相邻节点的LCA上打上-1标记,这样询问就转化成求子树和。

    数据规模比较大,倍增LCA会被卡..

    其实正解应该建虚树...这样的复杂度还是比较玄学....建虚树应该是比较保险的方法.

    Code

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    inline int read()
    {
        int x=0; char ch=getchar();
        while (ch<'0' || ch>'9') ch=getchar();
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x;
    }
    #define MAXN 2000100
    int N,Q,id[MAXN];
    char S[MAXN];
    namespace ACMachine
    {
        struct TrieNode{int son[MAXN][26],fail[MAXN],end[MAXN];}trie;
        int sz=1;
        inline void Insert(int x)
        {
            int now=1,len=strlen(S+1);
            for (int i=1; i<=len; i++)
                if (trie.son[now][S[i]-'a']) now=trie.son[now][S[i]-'a'];
                    else now=trie.son[now][S[i]-'a']=++sz,now=sz;
            trie.end[now]=1; 
            id[x]=now;
        }
        inline void Buildfail()
        {
            queue<int>q; q.push(1);
            while (!q.empty())
                {
                    int now=q.front(); q.pop();
                    for (int i=0; i<26; i++)
                        if (trie.son[now][i])
                            {
                                int fa=trie.fail[now];
                                while (fa && !trie.son[fa][i]) fa=trie.fail[fa];
                                trie.fail[trie.son[now][i]]=fa? trie.son[fa][i] : 1;
                                q.push(trie.son[now][i]);
                            }
                }
        }
    }
    using namespace ACMachine;
    namespace FailTree
    {
        struct EdgeNode{int next,to;}edge[MAXN<<1];
        int head[MAXN],cnt;
        inline void AddEdge(int u,int v) {cnt++; edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt;}
        inline void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);}
        inline void BuildTree() {for (int i=2; i<=sz; i++) InsertEdge(trie.fail[i],i);}
    }
    using namespace FailTree;
    namespace Divide
    {
        int size[MAXN],deep[MAXN],fa[MAXN],son[MAXN],pl[MAXN],dfn,pre[MAXN],top[MAXN],pr[MAXN];
        inline void DFS_1(int now,int last)
        {
            size[now]=1;
            for (int i=head[now]; i; i=edge[i].next)
                if (edge[i].to!=last)
                    {
                        deep[edge[i].to]=deep[now]+1;
                        fa[edge[i].to]=now;
                        DFS_1(edge[i].to,now);
                        size[now]+=size[edge[i].to];
                        if (size[son[now]]<size[edge[i].to]) son[now]=edge[i].to;
                    }
        }
        inline void DFS_2(int now,int chain)
        {
            pl[now]=++dfn; pre[dfn]=now; top[now]=chain;
            if (son[now]) DFS_2(son[now],chain);
            for (int i=head[now]; i; i=edge[i].next)
                if (edge[i].to!=fa[now] && edge[i].to!=son[now])
                    DFS_2(edge[i].to,edge[i].to);
            pr[now]=dfn;
        }
        inline int LCA(int x,int y)
        {
            while (top[x]!=top[y])
                {
                    if (deep[top[x]]<deep[top[y]]) swap(x,y);
                    x=fa[top[x]];
                }
            return deep[x]<deep[y]? x:y;
        }
    }
    using namespace Divide;
    namespace BIT
    {
        int tree[MAXN];
        inline int lowbit(int x) {return x&-x;}
        inline void Modify(int p,int d) {for(int i=p; i<=sz; i+=lowbit(i)) tree[i]+=d;}
        inline int Query(int p) {int re=0; for (int i=p; i; i-=lowbit(i)) re+=tree[i]; return re;}
        inline int Query(int l,int r) {return Query(r)-Query(l-1);}
    }
    using namespace BIT;
    int st[MAXN],tp,to;
    inline bool cmp(int x,int y) {return pl[x]<pl[y];}    
    int main()
    {
        N=read();
        for (int i=1; i<=N; i++) scanf("%s",S+1),ACMachine::Insert(i);
        ACMachine::Buildfail();
        FailTree::BuildTree();
        DFS_1(1,0); DFS_2(1,1);
        Q=read();
        while (Q--)
            {
                int opt=read(),le; char str[MAXN];
                switch (opt)
                    {
                        case 1: 
                            scanf("%s",str+1); le=strlen(str+1); tp=to=0;
                            for (int now=1,i=1; i<=le; i++)
                                {
                                    while (now && !trie.son[now][str[i]-'a']) now=trie.fail[now];
                                    now=trie.son[now][str[i]-'a'];
                                    if (now!=1) st[++tp]=now;
                                }
                            sort(st+1,st+tp+1,cmp);
                            st[to]=-1; for (int i=1; i<=tp; i++) if (st[to]!=st[i]) st[++to]=st[i];
                            for (int i=1; i<=to; i++) Modify(pl[st[i]],1);
                            for (int i=2; i<=to; i++) Modify(pl[LCA(st[i-1],st[i])],-1);
                        break;
                        case 2: int x=read(); printf("%d
    ",Query( pl[id[x]] , pr[id[x]] )); break;
                    }
            }
        return 0;
    }

    想找个机会整理一下fail树的一些性质....

  • 相关阅读:
    C++中的指针常量与常量指针
    Ubuntu16.04下安装ROS kinetic常见问题及解决方法
    关于安装ROS的资料备份
    后台模块--删除、修改用户信息
    客车网上售票系统--查询、添加用户
    客车网上售票系统--登录
    客车网上售票系统--需求分析(一)
    简单的邮件发送器(二)
    简单的邮件发送器(一)
    在CMD上用telnet远程登录发送邮件测试记录
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5993560.html
Copyright © 2011-2022 走看看