P2536 [AHOI2005]病毒检测
题意:给出一个病毒串;给出n个询问串,问这些串是病毒串否?
要我们找出不是病毒的串
思路:我们将询问串建字典树,然后来dfs跑这个病毒串 (bfs也可以)
那么如何跑呢?
假如病毒串当前位置是A T G C ,就判断是否能在字典树上继续往下走,不可以就直接返回
假如是问号,那么就有4种可能,我们也枚举一下,然后往下跑即可
假如是*号,我们就有多种可能 第一种是直接忽略,跑病毒串的下一个字符
第二种是像问号一样,枚举四种字符,然后跑下一个字符
第三种也是枚举4种字符,但不是跑一下字符,而是继续在这个字符停留(因为*可以表示无数个字符)
然后凡是能跑完病毒串的,就是病毒(不满足病毒串的率先return了)
那么跑完的判定条件是什么呢?
自然是跑到病毒串长度+1的位置拉
然后最后再用总数减去这些病毒RNA数量,就是最后答案了
但是!这道题卡内存,我们用来标记的vis数组,得用bitset来表示才不会MLE
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=250010; 4 int trie[maxn][6],num[maxn]; 5 int tot,ans=0,n,m,L; 6 bitset<1001>vis[maxn];//请用bitset 不然会被卡空间 7 char s[1005],str[505]; 8 int ark(char c)//将字符处理为各个数字 9 { 10 if(c=='A') return 0; 11 if(c=='G') return 1; 12 if(c=='T') return 2; 13 if(c=='C') return 3; 14 if(c=='?') return 4; 15 return 5; 16 } 17 void make_trie(char sa[505])//建trie树 18 { 19 int now=0,len=strlen(sa); 20 for(int i=0;i<len;i++){ 21 int next=ark(sa[i]); 22 if(!trie[now][next]){ 23 trie[now][next]=++tot; 24 } 25 now=trie[now][next]; 26 } 27 num[now]++;//因为可能有重复字符串,所以这里是++,而不是赋为1 28 } 29 void dfs(int x,int now) 30 { 31 if(x==L)//如果匹配成功(一定为模式串结尾 ) 32 { 33 ans+=num[now]; 34 num[now]=0;//记得清0 不然就重复算了 35 return; 36 } 37 if(vis[now][x])//记搜 38 { 39 return; 40 } 41 vis[now][x]=1;//赋初值 42 if(ark(s[x])==4)//如果当前字符为? 43 { 44 for(int i=0;i<=3;i++){ 45 if(trie[now][i]){ 46 dfs(x+1,trie[now][i]);//将?当成任意一个字符匹配 47 } 48 } 49 } 50 if(ark(s[x])==5)//如果当前字符为* 51 { 52 dfs(x+1,now);//不匹配* 53 for(int i=0;i<=3;i++) 54 { 55 if(trie[now][i]) 56 { 57 dfs(x+1,trie[now][i]); 58 dfs(x,trie[now][i]); 59 } 60 } 61 } 62 if(trie[now][ark(s[x])])//如果当前字符为字母 63 { 64 dfs(x+1,trie[now][ark(s[x])]); 65 } 66 } 67 int main() 68 { 69 scanf("%s",s); 70 L=strlen(s)+1; //将病毒串长度+1,跑到这个位置就表明有该种类型的病毒 71 scanf("%d",&n); 72 for(int i=1;i<=n;i++){ 73 scanf("%s",str); 74 make_trie(str); 75 } 76 dfs(0,0); //爆搜 77 cout<<n-ans;//由于我们找的是病毒段,而题目求得是不是病毒段的串,就用 n-ans 78 return 0; 79 }
借鉴自:https://www.cnblogs.com/dreagonm/p/10457141.html