AC自动机 - Trie - dfs - 判环
刚开始看这题,只往字符串匹配的方向想,最后发现根本不需要匹配QwQ
观察AC自动机的匹配方式,发现在匹配一个无病毒字符串时,一直在执行cur=g[cur][s[i]-'0']
,当存在该字符串的时候,是在Trie树上遍历,若该节点没有对应的子节点,就是在跳fail,并且保证不会跳到一个病毒串的结束位置,我们称为危险节点
所以我们只需建好Trie图后,用dfs判环就行了,下面说几个细节
对于每个节点
x
x
x,如果fail[x]
是危险节点,说明点
x
x
x代表的字符串中含有病毒,所以
x
x
x也是危险节点
最后我们找出的环一定是从根节点遍历过来的,也就是说要满足根节点可以在不经过危险节点的情况下到达环上
在dfs的时候,不能直接跳fail,必须沿着g数组储存的边走,否则就不满足AC自动机匹配的方式
代码:
#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int Maxn=500000+20,Maxm=30020,inf=0x3f3f3f3f;
int n,idcnt;
int g[Maxn][2];
int fail[Maxn];
bool vis[Maxn],c[Maxn],flag[Maxn];
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
void ins(char *s)
{
int cur=0,len=strlen(s);
for(int i=0;i<len;++i)
{
int x=s[i]-'0';
if(!g[cur][x])g[cur][x]=++idcnt;
cur=g[cur][x];
}
c[cur]=1;
}
void bfs()
{
queue <int> q;
for(int i=0;i<2;++i)
{
if(!g[0][i])continue;
q.push(g[0][i]);
}
while(q.size())
{
int x=q.front();
q.pop();
for(int i=0;i<2;++i)
{
if(g[x][i])
{
fail[g[x][i]]=g[fail[x]][i];
c[g[x][i]] |= c[fail[g[x][i]]];
q.push(g[x][i]);
}
else g[x][i]=g[fail[x]][i];
}
}
}
void dfs(int x)
{
if(vis[x])
puts("TAK"),exit(0);
if(c[x])return;
vis[x]=1;
dfs(g[x][0]);
dfs(g[x][1]);
vis[x]=0;
}
int main()
{
n=read();
for(int i=1;i<=n;++i)
{
char a[Maxm];
scanf("%s",a);
ins(a);
}
bfs();
dfs(0);
puts("NIE");
return 0;
}