[题解]P2444 [POI2000] 病毒
题目核心是多模式匹配,所以考虑用对所有模式串建立AC自动机。
我们把自动机上,存在一个模式串作为前缀的节点,称作“危险节点”。
如果无限长的安全代码存在的话,匹配过程中Trie图上一定有节点会经过多次,即存在环;而且经过的所有节点都不是“危险节点”,否则就包含病毒代码段了。
所以我们只需要看Trie图上是否存在不包含危险节点的环,就能得到答案了。
可以用一个dfs实现这一过程。用\(vis\)来表示节点访问状态,初始全为\(0\):
- \(vis[u]=0\):\(u\)未被访问过。
- \(vis[u]=1\):\(u\)正在处理中。
- \(vis[u]=-1\):从\(u\)出发找不到环。
如果只用\(0,1\)而不用\(-1\)来剪枝优化,时间复杂度可能由线性退化到指数级,将无法通过hack数据。
bool dfs(int u){ if(vis[u]==1) return 1; if(vis[u]==-1) return 0; vis[u]=1; for(int i=0;i<C;i++)//C=2 if(!en[tr[u][i]]&&dfs(tr[u][i])) return 1;//en[x]=1表示x是危险节点 vis[u]=-1; return 0; }
最后就是如何判断某个节点是不是危险节点,这个可以在get_fail()
中处理出来。如果节点\(u\)是模式串的结尾,或者\(fail[u]\)是危险节点,那么\(u\)就是危险节点。
时间复杂度\(O(\sum|s|\times |\Sigma|)\)。
点击查看代码
#include<bits/stdc++.h> #define N 30010//节点数(模式串总长) #define C 2//字符集大小 using namespace std; int n,tr[N][C],fail[N],cnt; int q[N],head,tail,vis[N]; bool en[N]; string s; void ins(string s){ int p=0; for(char i:s){ int c=i-'0'; if(!tr[p][c]) tr[p][c]=++cnt; p=tr[p][c]; } en[p]=1; } void get_fail(){ head=0,tail=-1; for(int i=0;i<C;i++) if(tr[0][i]) q[++tail]=tr[0][i]; while(head<=tail){ int u=q[head++]; for(int i=0;i<C;i++){ int& v=tr[u][i]; if(v) fail[v]=tr[fail[u]][i],q[++tail]=v, en[v]|=en[fail[v]]; else v=tr[fail[u]][i]; } } } bool dfs(int u){ if(vis[u]==1) return 1; if(vis[u]==-1) return 0; vis[u]=1; for(int i=0;i<C;i++) if(!en[tr[u][i]]&&dfs(tr[u][i])) return 1; vis[u]=-1; return 0; } int main(){ cin>>n; for(int i=1;i<=n;i++){ cin>>s; ins(s); } get_fail(); if(dfs(0)) cout<<"TAK\n"; else cout<<"NIE\n"; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效