1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn = 35000; 4 5 struct Aho_Corasock_Automaton { 6 struct node { 7 int fail; // 失配指针 8 int son[2]; // 子节点的位置 9 bool danger; // 危险标记 10 }Trie[maxn]; 11 int cnt = 0; // Trie指针 12 void insert(char *s) { 13 int len = strlen(s); 14 int now = 0; // Trie当前指针 15 for (int i = 0; i < len; ++i) { 16 if (Trie[now].son[s[i]-'0'] == 0) { // 如果没有这个子节点 17 Trie[now].son[s[i]-'0'] = ++cnt; // 构造该节点 18 } 19 now = Trie[now].son[s[i]-'0']; 20 } 21 Trie[now].danger = true; 22 } 23 void get_fail() { // 构造fail指针 24 queue<int> que; 25 for (int i = 0; i < 2; ++i) { // 先提前构造第二层 26 if (Trie[0].son[i] != 0) { // 如果存在该节点 27 Trie[Trie[0].son[i]].fail = 0; // fail指向root节点 28 que.push(Trie[0].son[i]); // 加入队列 29 } 30 } 31 while (!que.empty()) { // bfs求fail指针 32 int u = que.front(); que.pop(); 33 for (int i = 0; i < 2; ++i) { 34 if (Trie[Trie[u].fail].danger == true) 35 Trie[u].danger = true; 36 if (Trie[u].son[i] != 0) { // 如果存在该子节点 37 // 子节点的fail指针指向当前节点的fail指针指向内容相同的子节点 38 Trie[Trie[u].son[i]].fail = Trie[Trie[u].fail].son[i]; 39 que.push(Trie[u].son[i]); // 加入队列 40 } 41 else { // 如果不存在这个子节点 42 // 当前节点的该子节点指向当前节点的fail指针指向的止隔子节点 43 Trie[u].son[i] = Trie[Trie[u].fail].son[i]; 44 } 45 } 46 } 47 } 48 }AC; 49 bool vis[maxn]; 50 bool dfs(int u) { 51 if (vis[u] == true) return true; 52 vis[u] = true; 53 if (!AC.Trie[AC.Trie[u].son[0]].danger) { 54 if (dfs(AC.Trie[u].son[0])) { 55 return true; 56 } 57 } 58 if (!AC.Trie[AC.Trie[u].son[1]].danger) { 59 if (dfs(AC.Trie[u].son[1])) { 60 return true; 61 } 62 } 63 vis[u] = false; 64 return false; 65 } 66 char t[maxn]; 67 int main() { 68 int n; scanf("%d",&n); 69 for (int i = 1; i <= n; ++i) { 70 scanf("%s",t); 71 AC.insert(t); 72 } 73 AC.get_fail(); 74 if (dfs(0) == true) puts("TAK"); // 从字典树的根结点开始找环 75 else puts("NIE"); 76 return 0; 77 }