题解
已知,
AC自动机是一种多模字符串匹配算法:
构造tire树后,在模式串末尾最后一位的节点上标记,
而平常的AC自动机,是尽量多的接触这些标记
但是,这道题要求是避免的接触这些结束标记
那么从根节点开始,往trie树上跑,能用失配指针跳转就跳转,然后就会发现你陷入了一个死循环,因为如果不是循环,最后结束的位置就一定会有某个病毒的结束标记
且,在处理trie树上的失配指针的时候,因为是bfs式探寻的,失配对象的状态先统计出来,假设其含有结束标记,而后面又有另外路径上节点匹配上它了,那当我们只走过那条路时,也是可以被视作有含有结束标记
比如这样:C是病毒,那么ABCD就不是安全的代码
所以,现在我们要做的就是找到一个环,环里没有任何结束标记
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n, m, k;
namespace AC {//AC自动机板子
const int maxn = 2;
struct Tree {//字典树
int fail;//失配指针
int vis[maxn];//子节点位置
int end;//标记有几个单词以这个节点
} AC[N];//trie树 根节点为0
int cnt = 0;//编号
void clear(int x) {//清空
memset(AC[x].vis, 0, sizeof(AC[x].vis));
AC[x].fail = 0;//结束标志 一般以0为root
AC[x].end = 0;
}
void build(string s) {
int len = s.length();
int now = 0;//字典树当前指针
for (int i = 0; i < len; ++i) {
if (AC[now].vis[s[i] - '0'] == 0) {
//trie树没有这个子节点
AC[now].vis[s[i] - '0'] = ++cnt;
clear(cnt);
}
now = AC[now].vis[s[i] - '0'];
}
AC[now].end = 1;//标记单词结尾
}
queue<int> q;
void get_fail() {//构造fail指针
for (int i = 0; i < maxn; ++i) {//第二层提前预处理
if (AC[0].vis[i]) {
AC[AC[0].vis[i]].fail = 0;//指向根节点
q.push(AC[0].vis[i]);
}
}
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 0; i < maxn; ++i) {
if (AC[u].vis[i]) {//存在子节点
AC[AC[u].vis[i]].fail = AC[AC[u].fail].vis[i];
//子节点的fail指针 指向父节点fail指针所指向的节点 的相同的子节点 vis[i]相同
//如果那个子节点不存在 相当于该子节点fail=0 指向了根节点
AC[AC[u].vis[i]].end |= AC[AC[AC[u].fail].vis[i]].end;
q.push(AC[u].vis[i]);
} else {
//不存在子节点
AC[u].vis[i] = AC[AC[u].fail].vis[i];
//当前节点的子节点 指向 当前节点fail指针指向的节点的子节点
//把当前节点fail指向的节点 的子节点 作为自己的子节点
}
}
}
}
int AC_Query(string s) {//这里的s是被匹配的文本串
int len = s.length();
int now = 0, ans = 0;
for (int i = 0; i < len; ++i) {
now = AC[now].vis[s[i] - '0'];
for (int j = now; j && AC[j].end != -1; j = AC[j].fail) {
//j不能延伸到根节点 且AC[j]不能是走过的
ans += AC[j].end;
AC[j].end = -1;
}
}
return ans;
}
void init() {
cnt = 0;
clear(0);
}
}
using namespace AC;
bool v1[N];//判断是否是当前搜索路径上的一点
bool v2[N];//判断是历史路径上搜过的点
void dfs(int x) {
if (v1[x]) {//环
puts("TAK");
exit(0);
}
//如果又重回到曾经走不通的路径里 或者当前路径有结束标记
if (v2[x] || AC::AC[x].end) return;
v1[x] = 1;
v2[x] = 1;
dfs(AC::AC[x].vis[0]);
dfs(AC::AC[x].vis[1]);
v1[x] = 0;
}
string s;
int main() {
ios::sync_with_stdio(0);
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> s;
build(s);
}
get_fail();
dfs(0);
puts("NIE");
return 0;
}