HDU2222 Keywords Search(AC自动机模板)
2016-01-27 20:26
429 查看
AC自动机是一种多模式匹配的算法。大概过程如下:
首先所有模式串构造一棵Trie树,Trie树上的每个非根结点都代表一个从根出发到该点路径的字符串。
然后每个结点都计算出其fail指针的值,这个fail指针就指向这个结点所表示字符串的最长存在的后缀所对应的结点,如果不存在就指向根:计算每个结点的fail用BFS,比如当前结点u出队要拓展并计算其孩子结点的fail,v是其第k个孩子,fail[v]的值就是某个fail[fail[fail...[u]]]存在第k孩子结点其第k个孩子结点,如果不存在fail[v]就等于root。
最后主串就往Trie树上跑,在某个Trie树结点失配了就跳转到这个结点fail指针所指的结点继续跑——不过如果匹配了某个模式串这时可能某个模式串的后缀串被忽略了,所以需要用到temp指针,去检查是否有遗漏后缀没匹配。
而这题大概就是给几个模式串,一个主串,问有几个模式串被主串匹配。
AC自动机的模板题。有个可以优化的地方就是某个模式串被匹配了,下一次经过这儿就可以跳过了temp指针的过程了。
代码参考自kuangbin巨的博客,太简洁了(300+ms):
另外之前学的指针版本的,指针版本跑得更快(200+ms):
首先所有模式串构造一棵Trie树,Trie树上的每个非根结点都代表一个从根出发到该点路径的字符串。
然后每个结点都计算出其fail指针的值,这个fail指针就指向这个结点所表示字符串的最长存在的后缀所对应的结点,如果不存在就指向根:计算每个结点的fail用BFS,比如当前结点u出队要拓展并计算其孩子结点的fail,v是其第k个孩子,fail[v]的值就是某个fail[fail[fail...[u]]]存在第k孩子结点其第k个孩子结点,如果不存在fail[v]就等于root。
最后主串就往Trie树上跑,在某个Trie树结点失配了就跳转到这个结点fail指针所指的结点继续跑——不过如果匹配了某个模式串这时可能某个模式串的后缀串被忽略了,所以需要用到temp指针,去检查是否有遗漏后缀没匹配。
而这题大概就是给几个模式串,一个主串,问有几个模式串被主串匹配。
AC自动机的模板题。有个可以优化的地方就是某个模式串被匹配了,下一次经过这儿就可以跳过了temp指针的过程了。
代码参考自kuangbin巨的博客,太简洁了(300+ms):
#include<cstdio> #include<cstring> #include<queue> using namespace std; int tn,ch[510000][26],cnt[510000],fail[510000]; void insert(char *s){ int x=0; for(int i=0; s[i]; ++i){ int y=s[i]-'a'; if(ch[x][y]==0) ch[x][y]=++tn; x=ch[x][y]; } ++cnt[x]; } void init(){ memset(fail,0,sizeof(fail)); queue<int> que; for(int i=0; i<26; ++i){ if(ch[0][i]) que.push(ch[0][i]); } while(!que.empty()){ int x=que.front(); que.pop(); for(int i=0;i<26;++i){ if(ch[x][i]) que.push(ch[x][i]),fail[ch[x][i]]=ch[fail[x]][i]; else ch[x][i]=ch[fail[x]][i]; } } } int query(char *s){ int x=0,res=0; for(int i=0; s[i]; ++i){ int tmp=x=ch[x][s[i]-'a']; while(tmp){ if(cnt[tmp]>=0){ res+=cnt[tmp]; cnt[tmp]=-1; }else break; tmp=fail[tmp]; } } return res; } char S[1100000],T[55]; int main(){ int t,n; scanf("%d",&t); while(t--){ tn=0; memset(ch,0,sizeof(ch)); memset(cnt,0,sizeof(cnt)); scanf("%d",&n); while(n--){ scanf("%s",T); insert(T); } init(); scanf("%s",S); printf("%d\n",query(S)); } return 0; }
另外之前学的指针版本的,指针版本跑得更快(200+ms):
#include<cstdio> #include<cstring> #include<queue> using namespace std; typedef struct Node *pNode; struct Node{ int cnt; pNode fail,nxt[26]; Node(){ cnt=0; fail=NULL; for(int i=0;i<26;++i) nxt[i]=NULL; } }; pNode root; char S[1000100]; void insert(char *s){ pNode p=root; for(int i=0;s[i];++i){ int index=s[i]-'a'; if(p->nxt[index]==NULL){ p->nxt[index]=new Node; } p=p->nxt[index]; } ++p->cnt; } void init(){ queue<pNode> que; que.push(root); while(que.size()){ pNode y=que.front(); que.pop(); for(int i=0;i<26;++i){ if(y->nxt[i]==NULL) continue; if(y==root){ y->nxt[i]->fail=root; que.push(y->nxt[i]); continue; } pNode x=y->fail; while(x&&x->nxt[i]==NULL) x=x->fail; if(x==NULL) y->nxt[i]->fail=root; else y->nxt[i]->fail=x->nxt[i]; que.push(y->nxt[i]); } } } int query(){ int res=0; pNode x=root; for(int i=0;S[i];++i){ int index=S[i]-'a'; while(x->nxt[index]==NULL&&x!=root) x=x->fail; x=x->nxt[index]; if(x==NULL) x=root; pNode y=x; while(y!=root){ if(y->cnt>=0){ res+=y->cnt; y->cnt=-1; }else break; y=y->fail; } } return res; } int main(){ int t,n; char s[55]; scanf("%d",&t); while(t--){ root=new Node; scanf("%d",&n); for(int i=0;i<n;++i){ scanf("%s",s); insert(s); } scanf("%s",S); init(); printf("%d\n",query()); } return 0; }
相关文章推荐
- EditText设置可以编辑和不可编辑状态
- spark-shell的wordcount的例子存档
- 《EffcativeSTL》
- dp最长公共子序列
- poj-1201 Intervals
- 2016/01/27 Head First Java 第一节小结
- 【Web前端】:JavaScript操作Cookie实现“历史搜索”
- Divide and conquer:Garland(POJ 1759)
- CSS定位absolute和relative
- spark load file的几种方式
- Fedora21修改jdk为1.7版并安装Eclipse
- 在Windows上安装MongoDB
- Coursera课程《大家的python》(Python for everyone)课件
- qcow2、raw、vmdk等镜像格式
- hdu 1800 Flying to the Mars 字典树
- 《Boost程序完全开发指南》
- [iOS]iPhone利用<极光推送>实现远程推送
- OC加强day04-NSString、array、增强for、block、.h中协议的声明
- 【HNOI2008】【BZOJ1008】越狱
- qemu-kvm virtio 虚拟化-----Linux客户机 virtio设备初始化