洛谷P3121 【[USACO15FEB]审查(黄金)Censoring (Gold)】

双栈+AC自动机

这题其实跟一道KMP算法的题有一些渊源,它就是这道题的简单板。

Clear:
给你两个串A,B,每次在B串中从左到右找串A,并将该子串删除,直到找不到为止,问你能删几次。

样例输入:
abc

abcabcabaabcbccc

样例输出:
5

思路:
开一个栈,每次放入被匹配字符串的一个字符。如果当前栈中字符数量大于等于匹配串的长度,开始匹配,如果有一个单词匹配失败,break掉,继续放字符。

#include<bits/stdc++.h>
using namespace std;
int lc,lb,cnt,flag,ans;
char a[10000001];
string b,c;
int main()
{
    cin>>b>>c;
    lb=b.size();
    lc=c.size();
    for(int i=0;i<lc;i++)
    {
        a[++cnt]=c[i];
        if(cnt<b.size())
        {
            continue;
        }
        if(a[cnt]!=b[lb-1])
        {
            continue;
        }
        flag=0;
        for(int i=cnt-lb+1,j=0;i<cnt;i++,j++)//匹配
        {
            if(a[i]!=b[j])
            {
                flag=1;//发现目标串,标记。
                break;
            }
        }
        if(!flag)
        {
            ans++;
            cnt-=lb;//减长度
        }
    }
    printf("%d\n",ans);//输出
    return 0;
}

那么经过这题的思考之后,加强版(就是这题)的思路也应该油然而生了——我们同样用栈做,一个栈命名为s2,用来表示当前节点跑到了AC自动机中的trie树哪里了,另一个栈s3,用来表示最后的字符串留下了原字符串的哪些位上的字符。

如果发现当前这一个栈中的字符的后缀是单词,直接减去单词的长度(所以isword要存的是长度)。

剩下就是输出了。

友情提醒:在洛谷提交的话,末尾最好加个‘\n’。

代码:

#include<bits/stdc++.h>
using namespace std;
int cnt,s2[100010],s3[100010],n,wei,top;//s2是存root,s3是存剩下的。
string s1,s;
queue<int>q;
struct data
{
    int b[26],fail,isword;//isword用来存s.size。
}a[1000001];
void build(string t)//建trie树
{
    int root=0;
    for(int i=0;t[i];i++)
    {
        int x=t[i]-'a';
        if(!a[root].b[x])a[root].b[x]=++cnt;
        root=a[root].b[x];
    }
    a[root].isword=t.size();
}
int main()
{
    cin>>s;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        cin>>s1;
        build(s1);
    }
    for(int i=0;i<26;i++)
    {
        if(a[0].b[i])
        {
            q.push(a[0].b[i]);
        }
    }
    while(!q.empty())
    {
        int k=q.front();
        q.pop();
        for(int i=0;i<26;i++)
        {
            if(a[k].b[i])
            {
                a[a[k].b[i]].fail=a[a[k].fail].b[i];
                q.push(a[k].b[i]);  
            }else{
                a[k].b[i]=a[a[k].fail].b[i];
            } 
        }
    }
    int root=0;
    //双栈走起
    while(wei<s.size())
    {
        int x=s[wei]-'a';
        root=a[root].b[x];
        s2[++top]=root;
        s3[top]=wei;
        if(a[root].isword)
        {
            top-=a[root].isword;
            if(top==0)
            {
                root=top;
            }else{
                root=s2[top];
            }
        }
        wei++;
    }
    for(int i=1;i<=top;i++)
    {
        cout<<s[s3[i]];
    }
    putchar('\n');
    return 0;
}

QAQAQAQ

转载于:https://www.cnblogs.com/2017gdgzoi44/p/11323210.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值