Luogu P2536 [AHOI2005]病毒检测

题意

给一个有通配符的模式串和 \(n\) 个文本串,其中 ? 可以匹配任意字符,* 可以匹配 \(0\) 或任意多个字符,求 \(n\) 个文本串中无法与模式串匹配的数量。

\(\texttt{Data Range:}1\leq n\leq 500\)

题解

非确定性有限状态决策自动机。

首先考虑对模式串建一个自动机。对于每一位我们可以抽象成一个节点,整个字符串可以抽象成一个转移图。在匹配的过程中,如果能够走到这个节点上来就说明能够与这个位置的前缀相匹配。这里需要讨论一下:

  • 如果模式串的某一位为字母,那么将这一位的状态向下一位的状态连这个字母的边,也就是说只有这个字母能从这一位转移到下一位。

  • 如果模式串的某一位为 ?,那么任何字母进来都能匹配上,也就是说将这一位的状态向下一位的状态连所有字母的边。

  • 如果模式串的某一位为 *,这里是个小 trick,也就是说可以匹配任意长度的串,那就连个自环就可以了。

然后,对于匹配的话,从初始状态开始走,如果能够走到结束状态那么则匹配成功。

代码非常好写,跑得也很快。

代码

#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=1e3+51;
struct Node{
    ll isEnd;
    vector<ll>nxt[4];
};
Node x[MAXN];
vector<ll>s,nxt;
ll n,len,tot,c,flg,res;
ll mp[128],vis[MAXN];
char ch[MAXN];
inline ll read()
{
    register ll num=0,neg=1;
    register char ch=getchar();
    while(!isdigit(ch)&&ch!='-')
    {
        ch=getchar();
    }
    if(ch=='-')
    {
        neg=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        num=(num<<3)+(num<<1)+(ch-'0');
        ch=getchar();
    }
    return num*neg;
}
inline void match()
{
    s.clear(),s.push_back(1);
    for(register int i=1;i<=len;i++)
    {
        nxt.clear(),flg=0;
        for(register int j=1;j<=tot;j++)
        {
            vis[j]=0;
        }
        for(register int j=0;j<s.size();j++)
        {
            for(register int k=0;k<x[s[j]].nxt[mp[ch[i]]].size();k++)
            {
                if((c=x[s[j]].nxt[mp[ch[i]]][k])&&!vis[c])
                {
                    vis[c]=1,nxt.push_back(c),flg|=x[c].isEnd;
                }
            }
        }
        s=nxt;
    }
    res+=1-flg;
}
int main()
{
    scanf("%s",ch+1),len=strlen(ch+1),tot=1,memset(mp,-1,sizeof(mp));
    mp['A']=0,mp['G']=1,mp['C']=2,mp['T']=3;
    for(register int i=1;i<=len;i++)
    {
        if(mp[ch[i]]!=-1)
        {
            x[tot].nxt[mp[ch[i]]].push_back(tot+1),tot++;
            continue;
        }
        if(ch[i]=='?')
        {
            x[tot].nxt[0].push_back(tot+1),x[tot].nxt[1].push_back(tot+1);
            x[tot].nxt[2].push_back(tot+1),x[tot].nxt[3].push_back(tot+1);
            tot++;
            continue;
        }
        if(ch[i]=='*')
        {
            x[tot].nxt[0].push_back(tot),x[tot].nxt[1].push_back(tot);
            x[tot].nxt[2].push_back(tot),x[tot].nxt[3].push_back(tot);
            continue;
        }
    }
    x[tot].isEnd=1,n=read();
    for(register int i=0;i<n;i++)
    {
        scanf("%s",ch+1),len=strlen(ch+1),match();
    }
    printf("%d\n",res);
}
posted @ 2020-10-29 08:59  Karry5307  阅读(436)  评论(1编辑  收藏  举报