【HDU2222】【Keywords Search】AC自动机,有详细注释题解。

来源:互联网 发布:jquery 数组添加对象 编辑:程序博客网 时间:2024/06/06 02:49

题意:给定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 26using 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 26using 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;}








1 0
原创粉丝点击