BZOJ2938:[Poi2000]病毒

BZOJ2938:[Poi2000]病毒

考虑AC自动机匹配的过程

如果下一个节点是危险节点,我们就不跳到这个节点

如果下一个节点的 fail 是危险节点,我们也不跳到这个节点

这个标记在 getfail 的时候就可以打上

这样只要匹配的过程中能构成一个循环即可

非常的妙啊

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cstdio>
#include<queue>
using namespace std;

const int MAXN = 1000005;

struct Node{
    int fail, nxt[2], lst;
    bool end;
    Node(){fail = lst = 0; memset(nxt, 0, sizeof(nxt));end = false;}
}t[MAXN];
int n, Root = 0, ptr = 0;
char str[MAXN];
bool getans = false, inque[MAXN], vis[MAXN];
queue<int> q;

inline int newnode() {
    return ++ptr;
}
inline void Insert(char *s) {
    int len = strlen(s), cur = Root;
    for(int i = 0; i < len; ++i) {
        register int x = s[i] - '0';
        if(!t[cur].nxt[x]) t[cur].nxt[x] = newnode();
        cur = t[cur].nxt[x];
    }
    t[cur].end = true;
    return;
}
inline void getfail() {
    t[Root].fail = 0;
    for(int i = 0; i < 2; ++i) 
        if(t[Root].nxt[i]) 
            q.push(t[Root].nxt[i]);
    while(!q.empty()) {
        int cur = q.front(); q.pop();
        for(int i = 0; i < 2; ++i) {
            register int son = t[cur].nxt[i], fail = t[cur].fail;
            if(!son) {
                t[cur].nxt[i] = t[fail].nxt[i];
                continue;
            }
            t[son].fail = t[fail].nxt[i];
            t[son].end |= t[t[son].fail].end;
            q.push(son);
        }
    }
    return;
}
void dfs(int cur) {
    if(getans) return;
    inque[cur] = true;
    for(int i = 0; i < 2; ++i) {
        int son = t[cur].nxt[i];
        if(inque[son]) {
            getans = true;
            break;
        }
        if(t[son].end || vis[son]) continue;
        vis[son] = true;
        dfs(son);
    }
    inque[cur] = false;
    return;
}

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        scanf("%s", str);
        Insert(str);
    }
    getfail();
    dfs(Root);
    if(getans) puts("TAK");
    else puts("NIE");
    return 0;
}

 

转载于:https://www.cnblogs.com/xcysblog/p/9314803.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值