1. 程式人生 > >【[AHOI2005]病毒檢測】

【[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;
}