zoukankan      html  css  js  c++  java
  • 病毒「POI 2000」

    【题目描述】

    二进制病毒审查委员会最近发现了如下的规律:某些确定的二进制串是病毒的代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。现在委员会已经找出了所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。

    示例:例如如果 ({011, 11, 00000}) 为病毒代码段,那么一个可能的无限长安全代码就是 (010101cdots)。如果 ({01, 11, 000000}) 为病毒代码段,那么就不存在一个无限长的安全代码。

    请写一个程序,读入病毒代码,判断是否存在一个无限长的安全代码,将结果输出。

    【输入格式】

    第一行包括一个整数 (n),表示病毒代码段的数目;
    以下的 (n) 行,每一行都包括一个非空的 (01) 字符串——就是一个病毒代码段。

    【输出格式】
    假如存在这样的代码,则输出 TAK,否则输出 NIE。

    题解

    由于是AC自动机专题 所以我们不妨考虑一下怎么用AC自动机解决

    显而易见 这个无限长的(01)串应该是有循环节的

    (n)个危险串插入AC自动机 在结束节点打标记

    那么当你在AC自动机上对这个(01)循环串做匹配的时候,当前指针走的应该也是循环,并且 沿途经过的节点 以及 每个经过节点到根的fail链上 都没有结束节点

    因为用AC自动机做匹配的时候不是走到每个节点都要跳fail 如果跳到有结束tag的就(文章包含单词数)+1吗 而这里我们希望它为0

    所以我们把所有 有结束tag的节点 及 这个节点到根的fail链上有结束节点 的节点删掉(设为不可用)

    然后在剩下的节点构成的这个不完整的AC自动机里找环 如果找得到就是有解 否则无解 找环像找强连通分量那么找就行 或者就dfs一下也行

    注意特判一下自环

    【代码】

    #include <bits/stdc++.h>
    using namespace std;
    
    int n, len;
    char s[100005];
    
    int ch[100005][2], fail[100005], tot;
    bool tag[100005];
    
    inline void insert(char *str, int l) {
    	int x = 0;
    	for (int i = 1; i <= l; i++) {
    		if (!ch[x][str[i] - '0']) {
    			ch[x][str[i] - '0'] = ++tot;
    		}
    		x = ch[x][str[i] - '0'];
    	}
    	tag[x] = 1;
    }
    
    queue<int> q;
    
    inline void getfail() {
    	for (int i = 0; i < 2; i++) if (ch[0][i]) q.push(ch[0][i]);
    	while (!q.empty()) {
    		int x = q.front(); q.pop();
    		tag[x] |= tag[fail[x]];
    		for (int i = 0; i < 2; i++) {
    			if (ch[x][i]) fail[ch[x][i]] = ch[fail[x]][i], q.push(ch[x][i]);
    			else ch[x][i] = ch[fail[x]][i];
    		}
    	}
    }
    
    int dfn[30005], low[30005], stk[30005], tme, top;
    bool vis[30005], flag;
    
    void tarjan(int x) {
    	dfn[x] = low[x] = ++tme; 
    	vis[x] = 1; 
    	stk[++top] = x; 
    	for (int i = 0; i < 2; i++) {
    		if (tag[ch[x][i]]) continue;
    		int y = ch[x][i];
    		if (x == y) flag = 1;
    		if (!dfn[y]) {
    			tarjan(y);
    			low[x] = min(low[x], low[y]);
    		} else if (vis[y]) {
    			low[x] = min(low[x], dfn[y]);
    		}
    	}
    	if (dfn[x] == low[x]) {
    		int now = 0, totot = 0;
    		do {
    			now = stk[top--];
    			vis[now] = 0;
    			totot++;
    		} while (now != x);
    		if (totot > 1) flag = 1;
    	}
    }
    
    int main() {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; i++) {
    		memset(s, 0, sizeof(s));
    		scanf("%s", s+1); len = strlen(s+1);
    		insert(s, len);
    	}
    	getfail();
    	flag = 0;
    	tarjan(0);
    	if (flag) puts("TAK");
    	else puts("NIE");
    	return 0;
    }
    
  • 相关阅读:
    基于xmpp openfire smack开发之Android客户端开发[3]
    简单工厂,抽象工厂,工厂模式三者的对照
    【独具慧眼 推荐有礼】找出您心中的技术大牛 活动開始啦!
    ZOJ 3201
    具体解释站点沙盒期的原因表现与解决的方法
    工作,究竟意味着什么
    MVC之查询demo
    学习开淘宝网店
    快排的两种写法
    QTableWidget具体解释(样式、右键菜单、表头塌陷、多选等) (非代理)
  • 原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM38.html
Copyright © 2011-2022 走看看