[BFS]luogu P2536 [AHOI2005]病毒检测

题面

https://www.luogu.com.cn/problem/P2536

分析

考虑建一个像trie的东西,即向下一个节点连代表字母的边

那么对于 AGCT 就直接向下一个节点连边

对于 ? 则可以向下一个节点连所有字母的边

对于 * ,我们采用对通配符的惯用操作,向自己连所有字母的边,即构造自环,表示匹配的时候可以是任意串

那么匹配的时候,在匹配串里正常移动,模式串里BFS,但是会T

考虑优化,发现对于匹配串的当前位置转移到下一个位置,在模式串中的每个点只能到达一次

所以用滚动队列跑BFS,每次扩展状态的时候标记一个 added 数组即可,记得清空

同时跑到匹配串和模式串的结尾则是一个未知病毒

代码

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int N=510;
struct Graph {
    int v,w,nx;
}g[8*N];
int cnt,list[2*N],gcnt;
int n,m,ans;
char s[2*N];
bool added[2*N];

void Add(int u,int v,int w) {g[++cnt]=(Graph){v,w,list[u]};list[u]=cnt;}

void BFS() {
    queue<int> q[2];int e=0;
    while (!q[0].empty()) q[0].pop();while (!q[1].empty()) q[1].pop();
    q[e].push(0);
    for (int j=1;j<m;j++) {
        memset(added,0,sizeof added);
        while (!q[e].empty()) {
            int u=q[e].front();q[e].pop();
            for (int i=list[u];i;i=g[i].nx)
                if (g[i].w==s[j]&&!added[g[i].v]) added[g[i].v]=1,q[e^1].push(g[i].v);
        }
        e^=1;
    }
    while (!q[e].empty()) {
        int u=q[e].front();q[e].pop();
        for (int i=list[u];i;i=g[i].nx)
            if (g[i].w==s[m]&&g[i].v==gcnt) return;
    }
    ans++;
}

int main() {
    scanf("%s",s+1);m=strlen(s+1);
    for (int i=1;i<=m;i++) {
        switch (s[i]) {
            case '?':{
                Add(gcnt,gcnt+1,'A');Add(gcnt,gcnt+1,'G');Add(gcnt,gcnt+1,'C');Add(gcnt,gcnt+1,'T');gcnt++;
                break;
            }
            case '*':{
                Add(gcnt,gcnt,'A');Add(gcnt,gcnt,'G');Add(gcnt,gcnt,'C');Add(gcnt,gcnt,'T');
                break;
            }
            default:{
                Add(gcnt,gcnt+1,s[i]);gcnt++;
                break;
            }
        }
    }
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%s",s+1),m=strlen(s+1),BFS();
    printf("%d
",ans);
}
View Code
在日渐沉没的世界里,我发现了你。
原文地址:https://www.cnblogs.com/mastervan/p/14598426.html