You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
|
|
|
|
#include <cstdio>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
const int N = 1e6 + 10;
|
|
|
|
|
|
|
|
|
|
int n; //模式串数量
|
|
|
|
|
char s[N]; //模式串
|
|
|
|
|
|
|
|
|
|
// Trie树
|
|
|
|
|
int tr[N][2], idx;
|
|
|
|
|
int cnt[N];
|
|
|
|
|
void insert(char *s) {
|
|
|
|
|
int p = 0;
|
|
|
|
|
for (int i = 0; s[i]; i++) {
|
|
|
|
|
int t = s[i] - '0';
|
|
|
|
|
if (!tr[p][t]) tr[p][t] = ++idx;
|
|
|
|
|
p = tr[p][t];
|
|
|
|
|
}
|
|
|
|
|
cnt[p]++; //记录以p点为结尾的模式串数量+1,也就是说它是危险节点
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AC自动机
|
|
|
|
|
int q[N], ne[N];
|
|
|
|
|
void bfs() {
|
|
|
|
|
int hh = 0, tt = -1;
|
|
|
|
|
for (int i = 0; i < 2; i++) //将第一层存在的节点入队列
|
|
|
|
|
if (tr[0][i]) q[++tt] = tr[0][i];
|
|
|
|
|
|
|
|
|
|
while (hh <= tt) {
|
|
|
|
|
int p = q[hh++];
|
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
|
int t = tr[p][i];
|
|
|
|
|
if (!t)
|
|
|
|
|
tr[p][i] = tr[ne[p]][i];
|
|
|
|
|
else {
|
|
|
|
|
ne[t] = tr[ne[p]][i];
|
|
|
|
|
q[++tt] = t;
|
|
|
|
|
|
|
|
|
|
// tr[t][i]这个节点,它的失配指针指向的节点,也是危险节点的话,那么,当前节点也是危险节点
|
|
|
|
|
if (cnt[ne[t]]) cnt[t]++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// dfs在trie树上判环的代码模板
|
|
|
|
|
int st[N];
|
|
|
|
|
bool dfs(int u) { //在AC自动机上判环
|
|
|
|
|
if (st[u] == 1) return true; //如果在判环过程中发现重复访问的点,说明存在环
|
|
|
|
|
if (st[u] == -1) return false; //如果以前检查过这个点u,结果一条路跑过黑,都没有检查出来过有环,那么下次如果再让我检查点u,就直接返回结果就可以了
|
|
|
|
|
|
|
|
|
|
st[u] = 1; //当前路径上走过了点u
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i <= 1; i++) { //二进制,必然每个节点有两条分叉边,0和1
|
|
|
|
|
if (!cnt[tr[u][i]]) { //如果tr[u][i]这个点是危险点的话,就避让开;如果不是危险点的话,就继续探索它
|
|
|
|
|
if (dfs(tr[u][i])) return true; // 如果后续发现存在环,那么我也可以上报信息:我的后代子孙中发现环,也就是我这一脉中存在环;
|
|
|
|
|
//如果tr[u][i]及后续不存在环,那还需要继续检查,不能直接返回 dfs(tr[u][i]),这一块要注意写法,也算是一种代码模板,需要背诵下来
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
st[u] = -1; //一条道跑到黑,都没有找出后续节点中存在环,那么标识上,防止以后重复查询本节点
|
|
|
|
|
return false; //如果找到了环,中途就会返回true,都跑到这里了,表示没有找到环
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int main() {
|
|
|
|
|
//加快读入
|
|
|
|
|
ios::sync_with_stdio(false), cin.tie(0);
|
|
|
|
|
cin >> n;
|
|
|
|
|
for (int i = 1; i <= n; i++) {
|
|
|
|
|
cin >> s;
|
|
|
|
|
insert(s);
|
|
|
|
|
}
|
|
|
|
|
//构建AC自动机
|
|
|
|
|
bfs();
|
|
|
|
|
|
|
|
|
|
//从root出发,开始判断是不是存在环
|
|
|
|
|
dfs(0) ? puts("TAK") : puts("NIE");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|