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,有以下方法。
- '*'被空字符串替代:\(trie\) 上不走,母串走。
- '*'被非空字符串替代:母串不走,\(trie\) 上走,且四种方向能走的都走。
- '?':母串、\(trie\) 都走,且四种方向能走的都走。
- '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