【[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;
}
posted @ 2019-01-02 11:58  asuldb  阅读(138)  评论(0编辑  收藏  举报