首页 > 代码库 > 【HDU2222】【Keywords Search】AC自动机,有详细注释题解。
【HDU2222】【Keywords Search】AC自动机,有详细注释题解。
题意:给定N个单词,和一个字符串S,求这N个单词在字符串S中,有多少个出现过。
题解:AC自动机裸题一枚。
AC自动机是基于字典树的一种KMP思想高级算法,用于多字串匹配。就是把字典树建好,然后模仿KMP的前缀数组“pre[]”,在字典树内处理了一个fail(失败指针),失配时顺着往前找,并寄托于此以得到答案。
直接附代码,里面有详解。(数组模拟版!!!指针神马的都去回收站吧!)
结构体+注释版本:
#include <queue> #include <cstdio> #include <cstring> #include <algorithm> #define N 250010/*HDU上实际数据范围略小,正常应该开50W*/ #define M 26/*26个字母*/ using namespace std; struct Trie/*字典树节点结构体*/ { int next[M],fail;/*fail,失败指针*/ short cnt;/*当前节点是多少个单词的结尾*/ }trie[N]; int root,cnt,ans,n;/*根默认为0*/ char t[60],s[1001000];/*t是用于建树的临时输入数组*/ int i,u,v,temp,alp,star;/*此处全为临时变量*/ void init()/*每组测试数据要清一下!*/ { root=cnt=ans=0; memset(trie,0,sizeof(trie)); } void insert()/*建字典树*/ { scanf("%s",t+1);/*字典树建树过程不赘述了*/ for(temp=root,i=1;t[i];i++) { alp=t[i]-'a';/*当前枚举到字母转化成数字*/ if(!trie[temp].next[alp])trie[temp].next[alp]=++cnt; temp=trie[temp].next[alp]; } trie[temp].cnt++; } queue<int>q; void acauto()/*处理出失败指针*/ { while(!q.empty())q.pop(); /*(在函数内开队列,貌似就不需要清,就能省下时间, 但是退出时系统是一定会清掉这些数据的,所以反而要慢。)*/ q.push(root); while(!q.empty()) { u=q.front();q.pop(); for(i=0;i<M;i++)if(v=trie[u].next[i]) {/*在此赘述一下AC自动机失败指针代表字串含义: 就是当前节点所代表字符串的一个最长后缀, 此后缀满足有一个不相同的root连接前缀与之相同 没有深刻理解的童鞋可以手调几个数据,还是那句话:“手调有助于深刻理解” */ if(u==root)trie[v].fail=root;/*这里只是一个防错处理, 因为root连着的节点不可能有上述后缀*/ else { temp=trie[u].fail;/*从v的角度看,是找到了一个父亲的失败指针*/ while(temp&&!trie[temp].next[i])temp=trie[temp].fail; /*从这个失败指针为起点,寻找尽可能长的后缀,即找相同字母的节点*/ trie[v].fail=trie[temp].next[i]; /*然后失败指针被赋值*/ } q.push(v); } } } void handle() {/*此函数是依托AC自动机来解决多字串匹配问题。*/ scanf("%s",s+1); for(temp=root,i=1;s[i];i++) { alp=s[i]-'a'; while(temp&&!trie[temp].next[alp])temp=trie[temp].fail; /*if(trie[temp].next[alp])*/star=temp=trie[temp].next[alp]; /*本代码next不存在则=0,所以可以无视注释掉的if代码*/ while(trie[star].cnt) { ans+=trie[star].cnt; trie[star].cnt=0; star=trie[star].fail; } /*因为上面几行代码略有缺陷,锁业下面有点补救措施*/ /*可以在样例数据的基础上再加一个字串“h”即可卡掉不加下面两行的代码*/ /*原因:若一个点本身是单词结尾,失败指针指向不是, 而失败指针的失败指针又是单词结尾节点,哼哼~~~*/ ans+=trie[trie[star].fail].cnt; trie[trie[star].fail].cnt=0; } } /* 在此提供另一种得到答案的思想, 即看该节点扫没扫过。 for(temp=root,i=1;s[i];i++) { alp=s[i]-'a'; while(temp&&!trie[temp].next[alp])temp=trie[temp].fail; star=temp=trie[temp].next[alp]; while(star&&!visit[temp]) { ans+=trie[star].cnt; star=trie[star].fail; visit[temp]=1; } } */ int main() { // freopen("test.in","r",stdin); int i,g,_g; scanf("%d",&_g); for(g=1;g<=_g;g++) { init(); scanf("%d",&n); for(i=1;i<=n;i++)insert();/*建树*/ acauto();/*处理失败指针*/ handle();/*进行匹配得到答案*/ printf("%d\n",ans); } return 0; }
结构体无注释版:
#include <queue> #include <cstdio> #include <cstring> #include <algorithm> #define N 250010 #define M 26 using namespace std; struct Trie { int next[26],fail; short cnt; }trie[N]; int root,cnt,ans,n; char t[60],s[1001000]; int i,u,v,temp,alp,star; void init() { root=cnt=ans=0; memset(trie,0,sizeof(trie)); } void insert() { scanf("%s",t+1); for(temp=root,i=1;t[i];i++) { alp=t[i]-'a'; if(!trie[temp].next[alp])trie[temp].next[alp]=++cnt; temp=trie[temp].next[alp]; } trie[temp].cnt++; } queue<int>q; void acauto() { while(!q.empty())q.pop(); q.push(root); while(!q.empty()) { u=q.front();q.pop(); for(i=0;i<M;i++)if(v=trie[u].next[i]) { if(u==root)trie[v].fail=root; else { temp=trie[u].fail; while(temp&&!trie[temp].next[i])temp=trie[temp].fail; trie[v].fail=trie[temp].next[i]; } q.push(v); } } } void handle() { scanf("%s",s+1); for(temp=root,i=1;s[i];i++) { alp=s[i]-'a'; while(temp&&!trie[temp].next[alp])temp=trie[temp].fail; star=temp=trie[temp].next[alp]; while(trie[star].cnt) { ans+=trie[star].cnt; trie[star].cnt=0; star=trie[star].fail; } ans+=trie[trie[star].fail].cnt; trie[trie[star].fail].cnt=0; } printf("%d\n",ans); } void build() { scanf("%d",&n); for(int i=1;i<=n;i++)insert(); } int main() { int i,g,_g; scanf("%d",&_g); for(g=1;g<=_g;g++) { init(); build(); acauto(); handle(); } return 0; }
数组模拟结构体无注释版(代码短,就是短):
#include <queue> #include <cstdio> #include <cstring> #include <algorithm> #define N 250010 #define M 26 using namespace std; int next[N][26],fail[N]; short cnt[N]; int root,num,ans,n; char t[60],s[1001000]; int i,u,v,temp,alp,star; void init() { root=num=ans=0; memset(fail,0,sizeof(fail)); memset(cnt,0,sizeof(cnt)); } void insert() { scanf("%s",t+1); for(temp=root,i=1;t[i];i++) { alp=t[i]-'a'; if(!next[temp][alp])next[temp][alp]=++num; temp=next[temp][alp]; } cnt[temp]++; } queue<int>q; void acauto() { while(!q.empty())q.pop(); q.push(root); while(!q.empty()) { u=q.front();q.pop(); for(i=0;i<M;i++)if(v=next[u][i]) { if(u==root)fail[v]=root; else { temp=fail[u]; while(temp&&!next[temp][i])temp=fail[temp]; fail[v]=next[temp][i]; } q.push(v); } } } void handle() { scanf("%s",s+1); for(temp=root,i=1;s[i];i++) { alp=s[i]-'a'; while(temp&&!next[temp][alp])temp=fail[temp]; star=temp=next[temp][alp]; while(cnt[star]) { ans+=cnt[star]; cnt[star]=0; star=fail[star]; } ans+=cnt[fail[star]]; cnt[fail[star]]=0; } printf("%d\n",ans); } void build() { scanf("%d",&n); for(int i=1;i<=n;i++)insert(); } int main() { int i,g,_g; scanf("%d",&_g); for(g=1;g<=_g;g++) { init(); build(); acauto(); handle(); } return 0; }
【HDU2222】【Keywords Search】AC自动机,有详细注释题解。
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。