洛谷2444:病毒
题意:
- 有n个二进制字符串,称为病毒。
- 构造一个二进制字符串,使得没有任何一个病毒出现在这个构造的二进制字符串中。
- 回答是否可以构造这样一个字符串。
思路:
- AC自动机。
- AC自动机是一个多模式匹配的数据结构。
- 我们首先构造\(trie\)树并构建\(fail\)指针。
- 这时候\(trie\)树就不再是\(trie\)树了,经过加了几个\(fail\)指针变成了一张有向图。
- 如果存在这样一个无限的字符串,使得没有任何一个病毒是他的子串,那么会有什么情况呢?
- 拿这个字符串到自动机上匹配,无论怎么样也到不了某个病毒串的结尾位置,因为构造的字符串无限长,所以他会在自动机里无限地转圈圈。
- 所以问题转化为:
- 在AC自动机上找一个环,这个环上没有节点是病毒串的结尾。
- 注意一个节点的\(fail\)节点如果是结尾节点,那么他自身也应该是一个结尾节点。
- 画个图看看
#include<bits/stdc++.h> using namespace std; const int maxn = 3e4 + 10; char s[maxn]; int n; struct AC_Automaton { int trie[maxn][5]; int val[maxn]; int fail[maxn]; int tot; void ins(char *str) { int len = strlen(str), p = 0; for(int k = 0; k < len; k++) { int ch = str[k] - '0'; if(trie[p][ch] == 0) trie[p][ch] = ++tot; p = trie[p][ch]; } val[p] = 1; } void build() { queue<int> q; for(int i = 0; i < 2; i++) { if(trie[0][i]) { //第二层指向根节点 fail[trie[0][i]] = 0; q.push(trie[0][i]); } } while(q.size()) { int x = q.front(); q.pop(); for(int i = 0; i < 2; i++) { if(trie[x][i]) { fail[trie[x][i]] = trie[fail[x]][i]; val[trie[x][i]] |= val[fail[trie[x][i]]]; q.push(trie[x][i]); } else trie[x][i] = trie[fail[x]][i]; } } } bool v1[maxn]; void dfs(int x) { if(v1[x]) { puts("TAK"); exit(0); } if(val[x]) return; v1[x] = 1; if(trie[x][0]) dfs(trie[x][0]); if(trie[x][1]) dfs(trie[x][1]); v1[x] = 0; } }AC; int main() { scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%s", s); AC.ins(s); } AC.build(); AC.dfs(0); //从字典树根节点开始找环 puts("NIE"); //不存在无限的串 return 0; }
来源:https://www.cnblogs.com/zxytxdy/p/12233485.html