洛谷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
",ans);//输出
	return 0;
}

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

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

剩下就是输出了。

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

代码:

#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('
');
    return 0;
}

QAQAQAQ

原文地址:https://www.cnblogs.com/2017gdgzoi44/p/11323210.html