题目
题解
这是一道比较锻炼思维的题,让我更加深的理解失配指针的作用
一般的AC自动机都是尽量多的匹配,这道题希望无限长,也就是希望能失配就失配,我们构造出trie树之后,在trie图(注意现在变成了一个图)上找环即可;
几点优化:根据trie树的性质.如果某个点的后缀是病毒,那么这个点肯定不能选
在dfs中,建立两个bool数组,一个需要回溯——用于在这条路径上找环,另一个不需要回溯——之前搜索过就不需要再搜索了
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <string>
using namespace std;
#define ll long long
const int maxn=2e6;
const int inf=1e9;
void debug(int *A,int len) {for (int i=1; i<=len; i++) printf("%d ",A[i]); printf("\n");}
int n,m;
string s;
struct Tree{
int fail;
int vis[30];
int end;
}AC[maxn];
int cnt=0;
void build_AC(string s)
{
int l=s.length(); int now=0;
for (int i=0; i<l; i++)
{
if (!AC[now].vis[s[i]-'0'])
AC[now].vis[s[i]-'0']=++cnt;
now=AC[now].vis[s[i]-'0'];
}
AC[now].end++;
}
queue <int> q;
void Get_fail()
{
for (int i=0; i<=1; i++)
if (AC[0].vis[i]) q.push(AC[0].vis[i]);
while (!q.empty())
{
int now=q.front(); q.pop();
for (int i=0; i<=1; i++)
{
if (AC[now].vis[i])
{
AC[AC[now].vis[i]].fail=AC[AC[now].fail].vis[i];
if (AC[ AC[ AC[now].vis[i] ].fail ].end) AC[AC[now].vis[i]].end++;//根据Tire树的性质优化
q.push(AC[now].vis[i]);
}
else AC[now].vis[i]=AC[AC[now].fail].vis[i];
}
}
}
bool vis[maxn],used[maxn];
bool dfs(int x)
{
vis[x]=true;
for (int i=0; i<=1; i++)
{
int to=AC[x].vis[i];
if (vis[to]) return true;
if (used[to] || AC[to].end) continue;//之前搜索过或有病毒,不必再搜索
if (dfs(to)) return true;
}
vis[x]=false;
return false;
}
int main()
{
scanf("%d",&n);
for (int i=1; i<=n; i++)
{
cin>>s;
build_AC(s);
}
Get_fail();
if (dfs(0)) printf("TAK");
else printf("NIE");
return 0;
}
总结
1、转化的思想
2、dfs中两个bool数组,其实找环用topsort也可以
3、加快做题速度