D. 病毒
题目描述
输入格式
输出格式
样例
数据范围与提示
对trie图的理解还是没有那么透彻啊(以前就只是知道他比trie树快…)。
安全的串可以在AC自动机上在不匹配单词终点的情况下无限匹配(颓题解之前就想到这里), 在trie图上,如果能无线匹配那么意味着trie图上有环,dfs判环即可。
注意还有两个剪枝(zz的我刚开始居然用队列模拟深搜…):
1.单词终点不能走,那么fail指向单词终点的点也不能走。设一个单词终点为x,y->fail=x,那么root~x为root~y的后缀,y也不能走。将x设为危险节点,fail指向x的y也是危险节点,同理所有fail指向危险节点的点都是危险节点(之前一直不能理解这玩意儿有啥用),dfs时将危险节点剪掉。
2.由于一个节点可能会搜多次,所以将搜索过但是return 0的节点标记,剪掉。
#include<iostream> #include<cstdio> using namespace std; struct trie { int count; bool pd,danger,vis; trie *next[2],*fail; trie() { count=0;pd=0;danger=0;vis=0; next[1]=next[2]=fail=NULL; } }*q[100000],*root=new trie(); int head,tail; void insert(char s[],trie *root) { trie *p=root; int i=0,index; while(s[i]) { index=s[i]-'0'; if(p->next[index]==NULL)p->next[index]=new trie(); p=p->next[index]; i++; } p->count++; p->danger=1; } void build_ac(trie *root) { q[++tail]=root; while(head!=tail) { trie *p=q[++head]; trie *temp=NULL; for(int i=0;i<=1;i++) if(p->next[i]) { if(p==root) p->next[i]->fail=p; else { p->next[i]->fail=p->fail->next[i]; if(p->fail->next[i]->danger)p->next[i]->danger=1; } q[++tail]=p->next[i]; } else if(p==root) p->next[i]=root; else p->next[i]=p->fail->next[i]; } } bool dfs(trie *p) { p->pd=1; for(int i=0;i<=1;i++) { if(p->next[i]->pd)return 1; if(p->next[i]->danger || p->next[i]->vis)continue; if(dfs(p->next[i]))return 1; p->next[i]->vis=1; } p->pd=0; return 0; } int n; char keyword[30010]; char str[100000]; signed main() { //freopen("in.txt","r",stdin); cin>>n; for(int i=1;i<=n;i++) { scanf("%s",keyword); insert(keyword,root); } build_ac(root); if(dfs(root))cout<<"TAK"; else cout<<"NIE"; }