【Coel.解题报告】【生产力++!】[USACO15FEB]Censoring G

题前碎语

博客园可以直接把网易云音乐外链播放器放在边角,这一点还是很舒服的。
边写代码边听\(Yunomi\)的曲子也是一种享受呢!


题目梗概:[USACO15FEB]Censoring G

题目描述

FJ 把杂志上所有的文章摘抄了下来并把它变成了一个长度不超过 \(10^5\) 的字符串 \(s\)。他有一个包含 \(n\) 个单词的列表,列表里的 \(n\) 个单词记为 \(t_1 \cdots t_n\)。他希望从 \(s\) 中删除这些单词。

FJ 每次在 \(s\) 中找到最早出现的列表中的单词(最早出现指该单词的开始位置最小),然后从 \(s\) 中删除这个单词。他重复这个操作直到 \(s\) 中没有列表里的单词为止。注意删除一个单词后可能会导致 \(s\) 中出现另一个列表中的单词。

FJ 注意到列表中的单词不会出现一个单词是另一个单词子串的情况,这意味着每个列表中的单词在 \(s\) 中出现的开始位置是互不相同的。

请帮助 FJ 完成这些操作并输出最后的 \(s\)

输入格式

第一行是一个字符串,表示文章 \(s\)

第二行有一个整数,表示单词列表的单词个数 \(n\)

\(3\) 到第 \((n + 2)\) 行,每行一个字符串,第 \((i + 2)\) 行的字符串 \(t_i\) 表示第 \(i\) 个单词。

输出格式

输出一行一个字符串,表示操作结束后的 \(s\)


一看就知道是要写\(AC\)自动机的题目,所以不多说先建一棵\(Trie\)树,然后建立失配数组。然后对每个单词进行匹配,匹配到就删。
可是删除单词的操作怎么办呢?
建立一个栈,每次插入一个模式串中的字母,然后匹配。如果匹配到,就把对应的单词弹出。
需要注意的是,这题还要再给\(AC\)自动机开一个栈表示方向,所以不能直接用\(STL\)栈,而是自己手写一个。
代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 100050
#define maxc 26
using namespace std;
int n,tot=1,trie[maxn][maxc],fail[maxn],dep[maxn];
int stack[maxn],ans[maxn],top;//stack为ac自动机的栈,ans为答案的栈
char s[maxn],t[maxn];
queue<int>Q;
void insert(char *s)
{
    int u=0,len=strlen(s);
    for(register int i=0;i<len;i++)
    {
        int c=s[i]-'a';
        if(!trie[u][c])
            trie[u][c]=++tot;
        u=trie[u][c];
    }
    dep[u]=len;//把匹配串长度存下来,方便后面计算
}
void getFail()
{
	for(register int i=0;i<maxc;i++)
		if(trie[0][i])Q.push(trie[0][i]);
	while(!Q.empty())
	{
		int u=Q.front();
		Q.pop();
		for(register int i=0;i<maxc;i++)
		{
			if(trie[u][i])
			{
				fail[trie[u][i]]=trie[fail[u]][i];
				Q.push(trie[u][i]);
			}
			else trie[u][i]=trie[fail[u]][i];
		}
	}
}
inline void query(char *s)
{
	int len=strlen(s),u=0;
	for(register int i=0;i<len;i++)
	{
		int c=s[i]-'a';
		u=trie[u][c];
		ans[++top]=i;
		stack[top]=u;
		if(dep[u])
		{
			top-=dep[u];//前面把dep算出来就是为了这里更方便
			/*当然也可以把每个字符串存下来,在这里调用strlen计算*/
			u=top<=0?0:stack[top];//小心减过头
		}
	}
}
int main()
{
    scanf("%s%d",s,&n);
    for(register int i=1;i<=n;i++)
    {
        scanf("%s",t);
        insert(t);
    }
    getFail();
    query(s);
    for(register int i=1;i<=top;i++)
    {
    	printf("%c",s[ans[i]]);
	}
    return 0;
}

题后闲话

发现抄题目梗概的时候能直接查看源码复制,方便许多。
真是太好了\(qwq\)

posted @ 2021-12-02 18:54  秋泉こあい  阅读(37)  评论(0编辑  收藏  举报