【[AHOI2005]病毒检测】
\(Trie\) 树+搜索
我用的是\(dfs\)
首先对于将所有的RNA片段都建到\(Trie\)树里去,之后来匹配那个模板串就好了
如果是匹配的位置是字母,那么我们就继续往下匹配
如果是\(?\),我们必须要略过\(Trie\)树上的一位去匹配
如果是\(*\),我们可以先考虑直接忽略这一位,也可以直接把这一位当成\(?\)来看,或者是在\(Trie\)树上略过一位,但是在模板串上的匹配仍为当前位置,这样就能实现在\(Trie\)树上忽略多位进行匹配了
一旦某一位匹配成功了,我们如果还有状态到达这一步就没有什么必要了,所以开一个\(bitset\)来进行记忆化就好了
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<bitset>
#define re register
#define maxn 500005
int son[maxn][4],flag[maxn];
int n,m,cnt,L,ans;
char S[1001],T[1001];
std::bitset<1001> f[maxn];
inline int read()
{
char c=getchar();
int x=0;
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9')
x=(x<<3)+(x<<1)+c-48,c=getchar();
return x;
}
inline int ch(char p)
{
if(p=='A') return 0;
if(p=='G') return 1;
if(p=='T') return 2;
if(p=='C') return 3;
}
inline void ins()
{
int len=strlen(S+1);
int now=0;
for(re int i=1;i<=len;i++)
{
if(!son[now][ch(S[i])]) son[now][ch(S[i])]=++cnt;
now=son[now][ch(S[i])];
}
flag[now]++;
}
void dfs(int now,int t)
{
if(t==L+1)//匹配成功
{
ans+=flag[now];
flag[now]=0;
return;
}
if(f[now][t]) return;
f[now][t]=1;//记忆化
if(T[t]>='A'&&T[t]<='Z')
{
if(!son[now][ch(T[t])]) return;
dfs(son[now][ch(T[t])],t+1);
}//是字母,那么我们就继续往下匹配
else
{
if(T[t]=='?')
{
for(re int i=0;i<4;i++)
if(son[now][i]) dfs(son[now][i],t+1);//在Trie上忽略一位,同时模板串匹配位置加1
}
if(T[t]=='*')
{
dfs(now,t+1);//忽略*,即用0个字符来代替它
for(re int i=0;i<4;i++)
if(son[now][i]) dfs(son[now][i],t+1),dfs(son[now][i],t);
//第一个dfs直接把这一位当成$?$来看,第二个dfs就能实现一个*代替多个字符
}
}
}
int main()
{
scanf("%s",T+1);
L=strlen(T+1);
n=read();
for(re int i=1;i<=n;i++)
scanf("%s",S+1),ins();
dfs(0,1);
printf("%d\n",n-ans);
return 0;
}