[Luogu P3121] [BZOJ 3940] [USACO15FEB]审查(黄金)Censoring (Gold)

本文介绍了一种使用AC自动机处理字符串中特定单词删除的算法。通过建立AC自动机,实现对模板串中目标单词的有效定位和删除,最终输出处理后的字符串。此方法适用于字符串长度不超过10^5的场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

洛谷传送门
BZOJ传送门

题目描述

FJ把杂志上所有的文章摘抄了下来并把它变成了一个长度不超过 1 0 5 10^5 105的字符串 S S S。他有一个包含 n n n个单词的列表,列表里的 n n n个单词记为 t 1 . . . t N t_1...t_N t1...tN。他希望从 S S S中删除这些单词。

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

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

请帮助FJ完成这些操作并输出最后的 S S S

输入输出格式

输入格式:

第一行一个字符串 S S S

第二行一个正整数 n n n

以下 n n n行, 每行一个字符串, 表示 t 1 . . . t N t_1...t_N t1...tN, 长度总和不超过 1 0 5 10^5 105

输出格式:

一行一个字符串, 表示删除操作完成后的 S S S

输入输出样例

输入样例#1:
begintheescapexecutionatthebreakofdawn 
2 
escape 
execution 
输出样例#1:
beginthatthebreakofdawn 

解题分析

对模板串建立 A C AC AC自动机, 然后把匹配串跑一遍,用栈记录位置即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <cmath>
#include <queue>
#include <cctype>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100500
int fail[MX], l[MX], pos[MX];
int son[MX][26];
char mod[MX], dat[MX], sta[MX];
int n, root, ct, top;
std::queue <int> q;
IN void insert(char *str)
{
    int len = std::strlen(str);
    R int now = root, id;
    for (R int i = 0; i < len; ++i)
    {
        id = str[i] - 'a';
        if (!son[now][id]) son[now][id] = ++ct;
        now = son[now][id];
    }
    l[now] = len;
}
IN void build()
{
    R int now;
    for (R int i = 0; i < 26; ++i) if (son[root][i]) q.push(son[root][i]);
    W (!q.empty())
    {
        now = q.front(); q.pop();
        for (R int i = 0; i < 26; ++i) if (son[now][i]) fail[son[now][i]] = son[fail[now]][i], q.push(son[now][i]);
        else son[now][i] = son[fail[now]][i];
    }
}
IN void solve(char *str)
{
    int len = std::strlen(str);
    R int now = root, id;
    for (R int i = 0; i < len; ++i)
    {
        id = str[i] - 'a';
        now = son[now][id];
        sta[++top] = str[i], pos[top] = now;
        W (l[now])
        {
            top -= l[now];
            now = pos[top];
        }
    }
    for (R int i = 1; i <= top; ++i) printf("%c", sta[i]); puts("");
}
int main(void)
{
    scanf("%s%d", dat, &n);
    for (R int i = 1; i <= n; ++i)
    {
        scanf("%s", mod);
        insert(mod);
    }
    build();
    solve(dat);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值