洛谷P3121审查黄金 AC自动机+栈

题目描述:

FJ把杂志上所有的文章摘抄了下来并把它变成了一个长度不超过10^5的字符串S。他有一个包含n个单词的列表,列表里的n个单词记为t_1...t_N。他希望从S中删除这些单词。FJ每次在S中找到最早出现的列表中的单词(最早出现指该单词的开始位置最小),然后从S中删除这个单词。他重复这个操作直到S中没有列表里的单词为止。注意删除一个单词后可能会导致S中出现另一个列表中的单词FJ注意到列表中的单词不会出现一个单词是另一个单词子串的情况,这意味着每个列表中的单词在S中出现的开始位置是互不相同的请帮助FJ完成这些操作并输出最后的S

输入样例#1:

begintheescapexecutionatthebreakofdawn 

escape 
execution 

输出样例#1:

beginthatthebreakofdawn 

题解:AC自动机+栈。首先毫无疑问肯定是AC自动机去匹配串,删除操作可以通过栈来实现,对于模式串,当前这一位不是一个单词的结尾的话,就压入栈中,继续AC自动机过程,若当前这一位是一个单词的结尾,说明匹配到一个串,于是需要将栈中当前的单词所有弹出,所以需要单词长度,因此在建立树的时候在单词末尾节点维护一个长度信息,直接top-=len[i],继续匹配。(很气的是明明只要100+的时间居然跑了600+。。。。无语了)

总结:字符串的题一定要弄清楚过程!过程很重要!过程很重要!过程很重要!不管是kmp,AC自动机还是后缀数组,后缀自动机,只要将题目的关键条件往过程上靠,就一定没有问题。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#define N 100005
using namespace std;
queue<int> q;
char ptr[N],str[N];
int lost[N];
char BB[N];
int n;
int fail[N],date[500000][26],stack[N],top=0,tot=0,cnt[N],len[N];
int pt,st;
void build()
{
    for(int i=0;i<26;i++)
        if(date[0][i])
           q.push(date[0][i]);
    while(!q.empty())
    {
        int re=q.front();q.pop();
        for(int i=0;i<26;i++)
        {
            if(!date[re][i])
            {
                date[re][i]=date[fail[re]][i];
                continue;
            }
            q.push(date[re][i]);
            fail[date[re][i]]=date[fail[re]][i];
        }
    }
}
int main()
{
    //freopen("sss.in","r",stdin);
//    freopen("阿娇的方式.out","w",stdout);
    memset(date,0,sizeof(date));
    scanf("%s",ptr+1);pt=strlen(ptr+1);
    scanf("%d",&n);
    while(n--)
    {
        scanf("%s",str+1);st=strlen(str+1);
        int j=0;
        for(int i=1;i<=st;i++)
        {
            if(!date[j][str[i]-'a']) date[j][str[i]-'a']=++tot,BB[tot]=str[i];
            j=date[j][str[i]-'a'];
        }
        cnt[j]++;len[j]=st;
    }
//    for(int i=1;i<=tot;i++) cout<<i<<"  RANH  "<<BB[i]<<endl;
    build();
    int j=0;
    for(int i=1;i<=pt;i++)
    {
        stack[++top]=i; 
        j=date[j][ptr[i]-'a'];
        if(cnt[j])
        {
            int tmp=len[j];
            top-=len[j];
            j=lost[stack[top]];
        }else lost[i]=j;
        //cout<<top<<"    "<<j<<endl;
    }
    for(int i=1;i<=top;i++) cout<<ptr[stack[i]];
    return 0;
}

	
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值