【Ybt OJ】[字符串算法 第5章] AC自动机 [后半章]

45 篇文章 0 订阅
2 篇文章 0 订阅

「 「 字符串算法 」 」 5 5 5 A C AC AC自动机 ( ( ( 2 2 2 ) ) )
目录:

D.屏蔽词删除
E.病毒代码

D . D. D. 例题 4 4 4 屏蔽词删除

在这里插入图片描述
洛谷 l i n k link link

分析:

A C AC AC自动机 + + + 模拟
模 拟:
看文本串中 有没有模式串 有就直接抹掉 抹完 n o w now now要跳到文本串 从头开始到删除串 没有被抹 的最右端 ( ( (如样例 ) ) )
没有就找模式串就行了 至于删除 就是看标记 然后跳到前面没有标记的地方 标记的就是要删除的字符
其实这个删除部分可以写

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue> 
#include<bitset>
#define Ctnue continue
//#pragma GCC optimize(2)
#define reg register
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=1e5+5;
char s[N],ch[N];
int tot,n,rt[N],end[N];
bitset<N> k;  //没啥用的bitset 标记
struct Trie{
	int son[30],fail,size;
}trie[2*N];
queue<int> q;
void ins(char ovo[])
{
	int len=strlen(ovo),now(0);
	for(int i=0;i<len;++i)
	{
		int qwq=ovo[i]-'a';
		if(!trie[now].son[qwq]) trie[now].son[qwq]=++tot;
		now=trie[now].son[qwq];
	}
	trie[now].size=len;
}
void AC()
{
	for(int i=0;i<26;++i)
		if(trie[0].son[i])
		{
			q.push(trie[0].son[i]);
			trie[trie[0].son[i]].fail=0;
		}
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		for(int i=0;i<26;i++)
		{
			if(trie[now].son[i])
			{
				q.push(trie[now].son[i]);
				trie[trie[now].son[i]].fail=trie[trie[now].fail].son[i];
			}
			else trie[now].son[i]=trie[trie[now].fail].son[i];
		}
	}
}
void K(int r,int T)  //暴力找标记 删除
{
	int now=r;
	while(T--)
	{
		while(k[r]) r=rt[r];
		k[r]=1;
		r--;
	}
	rt[now]=r;
}
void Delete(char ovo[])
{
	int len=strlen(ovo),now=0;
	for(int i=0;i<len;i++)
	{
		int qwq=ovo[i]-'a';
		if(trie[now].son[qwq])
		{
			now=trie[now].son[qwq];
			if(trie[now].size)
			{
				K(i,trie[now].size);  //删除模式串
				now=end[rt[i]];  //now回跳
			}
		}
		else
		{
			while(!trie[trie[now].fail].son[qwq]&&now)
				now=trie[now].fail;
			if(!now) now=trie[now].son[qwq];
			else now=trie[trie[now].fail].son[qwq];  //继续找模式串
		}
		end[i]=now;
	} 
}
int main(){
	scanf("%s",&s);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",&ch);
		ins(ch);
	}
	AC();Delete(s);
	int lenth=strlen(s);
	for(int i=0;i<lenth;i++)
		if(!k[i]) putchar(s[i]);
	
	return 0;
}

E . E. E. 例题 5 5 5 病毒代码

在这里插入图片描述
洛谷 l i n k link link

分析:

先想想:如果构造出一个无限长的安全代码 再到 A C AC AC自动机去匹配 会出现什么情况 ? ?
那当一位位匹配时 永远不会跳到某个病毒代码的结尾位置 然后 A C AC AC自动机就会一直原地 t p tp tp

先管病毒代码的结尾位置叫 危险标记 因为匹配到结尾已表明出现了病毒代码段
所以 问题就变成 在 t r i e trie trie图上找一个 并且环上没有危险标记
而且要注意 从根节点出发 不经过任何危险标记 也能到达这个环(不然 A C AC AC自动机匹配啥
A C AC AC自动机性质:如果节点 x x x f a i l fail fail有危险标记 那这个点 x x x也是危险的 因为 f a i l fail fail x x x后缀

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue> 
#include<bitset>
#define Ctnue continue
//#pragma GCC optimize(2)
#define reg register
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=3e4+5;
struct Trie{
	int fail,num[2];
	bool in;
}trie[N];
int n,tot;
queue<int> q;
char ch[N];
bool vis[N],book[N];
void ins(char a[])
{
	int len=strlen(a),now=0;
	for(int i=0;i<len;i++)
	{
		int qwq=a[i]-'0';
		if(!trie[now].num[qwq]) trie[now].num[qwq]=++tot;
		now=trie[now].num[qwq];
	}
	trie[now].in=true;
}
void AC()
{
	if(trie[0].num[0]>0) q.push(trie[0].num[0]);
	if(trie[0].num[1]>0) q.push(trie[0].num[1]);  //0或1 进队列
	while(!q.empty())
	{
		int now=q.front();
		q.pop();
		for(int i=0;i<=1;i++)
		{
			if(trie[now].num[i]>0)
			{
				q.push(trie[now].num[i]);
				int k=trie[now].fail;
				while(k&&trie[k].num[i]==0) k=trie[k].fail;  //最长后缀&&根节点
				if(trie[k].num[i]==0) trie[trie[now].num[i]].fail=0;  //fail转移到根节点
				else{
					trie[trie[now].num[i]].fail=trie[k].num[i];
					if(trie[trie[k].num[i]].in) trie[trie[now].num[i]].in=1;
					//自己的后缀危险 自己也危险
				} 
			}else trie[now].num[i]=trie[trie[now].fail].num[i];
		}
	} 
}
void dfs(int x)
{
	vis[x]=1;
	for(int i=0;i<=1;i++)
	{
		if(vis[trie[x].num[i]]){puts("TAK");exit(0);}  //找到环了
		else if(!trie[trie[x].num[i]].in&&!book[trie[x].num[i]])
		{
			book[trie[x].num[i]]=1;
			dfs(trie[x].num[i]);
		}
	}
	vis[x]=0;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ch);
		ins(ch);
	}
	AC();dfs(0);
	puts("NIE");
	
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值