zoukankan      html  css  js  c++  java
  • 牛客网暑期ACM多校训练营(第九场) F Typing practice(AC自动机)

    题目链接:https://www.nowcoder.com/acm/contest/147/F  牛客网

    感谢:https://blog.csdn.net/LSD20164388/article/details/81778591

    题目描述 

    Niuniu is practicing typing.

    Given n words, Niuniu want to input one of these. He wants to input (at the end) as few characters (without backspace) as possible,
    to make at least one of the n words appears (as a suffix) in the text.
    Given an operation sequence, Niuniu want to know the answer after every operation.
    An operation might input a character or delete the last character.

    输入描述:

    The first line contains one integer n.
    In the following n lines, each line contains a word.
    The last line contains the operation sequence.
    '-' means backspace, and will delete the last character he typed.

    He may backspace when there is no characters left, and nothing will happen.

    1 <= n <= 4
    The total length of n words <= 100000

    The length of the operation sequence <= 100000

    The words and the sequence only contains lower case letter.

    输出描述:

    You should output L +1 integers, where L is the length of the operation sequence.

    The i-th(index from 0) is the minimum characters to achieve the goal, after the first i operations.
    示例1

    输入

    复制
    2
    a
    bab
    baa-
    

    输出

    复制
    1
    1
    0
    0
    0

    说明

    "" he need input "a" to achieve the goal.
    "b" he need input "a" to achieve the goal.
    "ba" he need input nothing to achieve the goal.
    "baa" he need input nothing to achieve the goal.
    "ba" he need input nothing to achieve the goal.
    示例2

    输入

    复制
    1
    abc
    abcd
    

    输出

    复制
    3
    2
    1
    0
    3

    说明

    suffix not substring.

    题意:给你n个串,n的大小不大于4,再给你一个串,作为询问串,问你在询问串中每多取一个字符作为子串,需要再在该串的后面加几个字母使得四个串中存在一个作为后缀,若取到的是-代表退后一步,即将上一步取到的字母舍去,空串也要考虑。
    思路:首先将四个串加入到AC自动机中,利用AC自动机建立树,像是之前写过的题,fail和next正常建立,有一点不同就是当该结点的fail结点end为1时,该节点的end也为1,可以类比DNA那道题的解法,至于为什么这里就不说了。
    然后就是很神奇的地方了,end为1的结点我们将他的ans设为1,然后更新上面每个点的ans,怎么更新后面说,ans记录的是这个点要再加几个字符可以变成存在后缀的满足要求的状态。这样的话,最后每个子串的结果就是这个串在树上跑完之后最后
    一个结点的ans[]-1。
    怎么更新呢,在咱们求最短路的时候,先将直接可以到达的点也就是end==1的点放在队列中,接下来这个点可以直接到达的点的ans[]等于这个点的ans[]+1,什么叫做可以直接到达呢,就是这个点的上一步是谁,回退回去就可以得到谁,利用vectot
    存起来。
    接下来对于每一步,我们都保存起来最后一步是哪个结点,接下来再一步就是这一步加了一步。
    写的不好,估计只有自己能看懂,上面的大佬写的很棒。
    代码如下:
    #include<queue>
    #include<stdio.h>
    #include<vector>
    #include<iostream>
    #include<string.h>
    #include<algorithm>
    
    using namespace std;
    
    const int maxn =  4*100000+5;       //字典树上最多有多少个点
    const int maxl = 100000+5;       //目标串的最大长度
    
    struct Trie
    {
        int Next[maxn][26], fail[maxn], End[maxn];
        int root, L;
        int ans[maxn];
        vector<int>V[maxn];
        queue<int>QQ;
        int res[maxl];
        int new_node()
        {
            for(int i=0; i<26; i++)
                Next[L][i] = -1;
            V[L].clear();
            End[L++] = 0;
            return L-1;
        }
        void init()
        {
            L = 0;
            root = new_node();
            while( !QQ.empty() )
                QQ.pop();
            memset(ans, 0, sizeof(ans));
        }
        void Insert(char buf[])
        {
            int len = strlen(buf);
            int now = root;
            for(int i=0; i<len; i++)
            {
                int tmp = buf[i]-'a';   ///是小写还是大写看题目要求
                if(Next[now][tmp] == -1)
                    Next[now][tmp] = new_node();
                now = Next[now][tmp];
            }
            End[now]++;                 ///记录这个点式多少个模式串的子串
            ans[now] = 1;
            QQ.push(now);
        }
        void build()
        {
            queue<int>Q;
            fail[root] = root;
            for(int i=0; i<26; i++)
            {
                if(Next[root][i] == -1)
                {
                    Next[root][i] = root;
                }
                else
                {
                    fail[Next[root][i]] = root;
                    Q.push(Next[root][i]);
                }
            }
            while( !Q.empty() )
            {
                int now = Q.front();
                Q.pop();
                if(End[fail[now]] != 0)
                {
                    End[now] = 1;
                    ans[now] = 1;
                    QQ.push(now);
                }
                for(int i=0; i<26; i++)
                {
                    if(Next[now][i] == -1)
                    {
                        Next[now][i] = Next[fail[now]][i];
                    }
                    else
                    {
                        fail[Next[now][i]] = Next[fail[now]][i];
                        Q.push(Next[now][i]);
                    }
                }
            }
            for(int i=0; i<L; i++)
            {
                for(int j=0; j<26; j++)
                {
                    if(Next[i][j] != i)
                    {
                        V[Next[i][j]].push_back(i);
                    }
                }
            }
            while( !QQ.empty() )
            {
                int f = QQ.front();
                QQ.pop();
                for(int i=0; i<V[f].size(); i++)
                {
                    int v = V[f][i];
                    if(ans[v]!=0) continue;
                    ans[v] = ans[f]+1;
                    QQ.push(v);
                }
            }
        }
        void solve(int len , int now , char c)
        {
                now = Next[now][c-'a'];
                res[len] = now;
        }
        void query(char buf[])
        {
            memset(res , 0 , sizeof(res));
    //        char tmp[maxl];
            int len = 0;
            int Len = strlen(buf);
            printf("%d
    " , ans[0]-1);
            for(int i=0; i<Len; i++)
            {
                if(buf[i]=='-')
                {
                    len--;
                }
                else
                {
                    len++;
                    solve(len , res[len-1] , buf[i]);
                }
                if(len < 0)
                    len = 0;
                printf("%d
    " , ans[res[len]]-1);
            }
        }
    };
    
    Trie AC;
    char buf[maxl];
    
    int main()
    {
        int t, n;
        while( scanf("%d", &n) != EOF )
        {
            AC.init();
            for(int i=0; i<n; i++)
            {
                scanf("%s", buf);
                AC.Insert(buf);
            }
            getchar();
            AC.build();
            gets(buf);
            AC.query(buf);
        }
    
        return 0;
    }
    
    /*
    4
    sheast
    shyash
    yaat
    easthy
    shetsy-hea-ast
    4
    4
    4
    3
    4
    4
    3
    4
    4
    3
    2
    3
    2
    1
    0
    
    
    2
    a
    ab
    ---
    1
    1
    1
    1
    */
  • 相关阅读:
    html的转码玉反转码
    获取url据对路径写法
    CSS 外边距合并
    页面禁制选中元素的 背景变蓝的通用写法
    centos7.3上安装oracle11.2.4RAC
    通过ansible检查所有服务器根目录磁盘使用情况
    解决es集群启动完成后报master_not_discovered_exception(hostname有错误)
    tidb4.0执行大型sql报没有tmp目录错处理(ERROR 1105 (HY000): open /tmp/1000_tidb/MC4wLjAuMDo0MDAwLzAuMC4wLjA6MTAwODA)
    aix磁盘创建pv、lv
    aix6.1安装oracle
  • 原文地址:https://www.cnblogs.com/Flower-Z/p/9496859.html
Copyright © 2011-2022 走看看