牛客网暑期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\n" , 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\n" , 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
*/

 

posted @ 2018-08-18 12:30  Flower_Z  阅读(252)  评论(0编辑  收藏  举报