其他分享
首页 > 其他分享> > P2536 [AHOI2005]病毒检测

P2536 [AHOI2005]病毒检测

作者:互联网

目录

题意描述

太复杂了,自己去看吧[AHOI2005]病毒检测

下面是闲聊部分,忙人请直接看到算法分析。

嗯,这是一道细节很多的题目,关键是...它卡常,所以我是靠吸氧过的...。

当然了,这不重要,重要的是这道题让我的解题有了新思路:在 \(trie\) 上跑 DFS/BFS

看上去...很不可思议,但是这是真的很强的一种思路。

然后就进入正题吧。

算法分析

首先要明白一个道理:不是病毒的数=总数-是病毒的数。(小学生都能理解吧)

所以...酱紫的话,我们的目标就变成了求病毒数。

然后字符串问题可以自(ku)然(si)而(ming)然(xiang)得到可以用 \(trie\)。

用 \(cnt[i]\) 表示 \(trie\) 中以 \(i\) 结尾的单词数量,然后每次遍历完整个 \(s[]\)(即带有 */? 的串)ans+=cnt[i],cnt[i]=0

然后注意 BFS 时以 在 \(trie\) 中的位置目前匹配到的长度 为关键字即可。

代码实现

我们称模式串(就是有 '*' '?' 的那个东东)为母串,进行 \(trie\) 上的 BFS,有以下方法。

  1. '*'被空字符串替代:\(trie\) 上不走,母串走。
  2. '*'被非空字符串替代:母串不走,\(trie\) 上走,且四种方向能走的都走。
  3. '?':母串、\(trie\) 都走,且四种方向能走的都走。
  4. 'A''T''G''C':母串、\(trie\) 都走。

然后注意一下,将母串 \(s[]\) 输入时改为 \(s[1\to n]\)(即 scanf("%s",s+1)),然后令 \(s[0]='?'\)。

因为 ? 表示四个方向均可替代,正好符合一开始时每个字母都有可能为首字母,从而省略了一个循环。

判断是否来过某个节点时一定要用 \(bitset\) 不然会 MLE。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<bitset>
#include<iostream>
#include<queue>
#include<map>
#define M 1010
#define maxn 400010
using namespace std;

char s[M],a[M];
int n,tot=1,trie[maxn][5],cnt[maxn];
queue<pair<int,int> >q;
map<char,int>c;
bitset<M>vis[maxn];

int read(){
	int x=0,f=1;char c=getchar();
	while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
	while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
	return x*f;
}

void insert(char* a){
	int p=1,len=strlen(a);
	for(int i=0;i<len;i++){
		int ch=c[a[i]];
		if(!trie[p][ch]) trie[p][ch]=++tot;
		p=trie[p][ch];
	}
	cnt[p]++;
	return;
}
//以上是正常的 trie 操作
void Push(int u,int v){
	if(vis[u][v] || v>strlen(s+1) || !u) return;
	vis[u][v]=1;
	q.push(make_pair(u,v));
	return;
}
//这是 push 的过程,写成函数好康很多
int main(){
	scanf("%s",s+1);
	s[0]='?';//上面提到的操作
	scanf("%d",&n);
	c['A']=1;c['C']=2;c['T']=3;c['G']=4;
	c['*']=5;c['?']=6;//map 的应用
	for(int i=1;i<=n;i++){
		scanf("%s",a);insert(a);
	}
	q.push(make_pair(1,0));
	vis[1][0]=1;
	int ans=0;
	while(!q.empty()){
		int now=q.front().first;
		int len=q.front().second;
		q.pop();
		if(s[len]=='*')
			for(int i=1;i<=4;i++)
				Push(trie[now][i],len);
        //注意一下,这个循环一定要在下面判断的前面,不然会有问题。(想一想,为什么)
		if(len==strlen(s+1)){
			ans+=cnt[now];cnt[now]=0;continue;
		}
		if(c[s[len+1]]<=4) Push(trie[now][c[s[len+1]]],len+1);
		else{
			for(int i=1;i<=4;i++) 
				Push(trie[now][i],len+1);
			if(s[len+1]=='*') Push(now,len+1);
		}
	}
	printf("%d\n",n-ans);//容斥基本操作
	return 0;
}

结语

好简单的样子,所以...没什么好说的。

完结撒花。

标签:...,cnt,AHOI2005,trie,病毒检测,P2536,int,母串,include
来源: https://www.cnblogs.com/lpf-666/p/12614254.html