题目链接 : http://acm.hdu.edu.cn/showproblem.php?pid=3065

题意:给出几个字符串,问这些字符串在主字符串中出现了几次;

思路:

做这道题之前发现对AC自动机的fail指针有一点误解,一开始的时候,我以为fail指针只会指向相邻的Trie树的一个枝干上的某个字符,不会指向自己的这一条枝干上的,什么意思呢? 比如两个字符串   dacd   cdc 显然,这两个字符串如果建立Trie树的时候,从第一个字符开始就已经分支了, 我最开始对fail的理解就是,d的fail指针只会指向第二个子字符串的 d,但事实上,fail最先指向的应该是第一个子字符串的第一个 d,因为存在这个误解,所以这道题卡了一段时间;如果fail指针不会指向自己这条Trie树枝干的话,就可能会有遗漏的可能,如果问题是查找一个子字符串在主字符串中出现过几次;假设主字符串是  acdacdacd, 当我们匹配到主字符串中第二个d的时候,就已经到第一个子字符串的末尾了,这时候就应该去访问第一个子字符串中最后一个d所指向的那个fail,假设指向的是第二个子字符串的d,如果还是匹配失败,又会继续访问第二个字符串的d的fail,显然这时候的fail指向的是root(根节点),然后就结束匹配了,但事实上,从主字符串中,第二个d的后面还有一个字符串可以匹配成功(dac d acd),这就是遗漏的情况;实际上的fail是如何运作的呢?

假设主字符串是   AAACBBGCF; 子字符串是  AA; AAC ;  GCF   CF ; (上图中边框为红色表示是一个子字符串的末尾,当匹配到这个地方的时候,个数 + 1)

用p来表示正在匹配的字符在主字符串的位置,从 1开始 ;p = 1;

p = 1的时候,Trie从root开始的,匹配第一个字符,root开始去看它的子节点有没有和p = 1的字符串对应的,发现左边第一行的A能和 p = 1的字符匹配,但并不是一个子字符串的末尾,然后开始访问 root的fail (fail的访问默认到root就结束的),访问结束后Trie中匹配的位置就是第一行的A这个位置,这时候p++,即p = 2,然后第一行的A开始去看它的子节点有没有和p = 2这个字符匹配的,显然是有的,而且是一个子字符串的末尾,所以个数+1,然后开始访问第一行的A的fail指针(按照一般情况,应该去查看这个fail指针指向的节点的子节点是否和当前匹配的字符为同一个字符,如果是而且是一个子字符串的末尾,那么个数+1,不管是不是末尾也要继续访问当前节点的fail,但是这里fail是root,模版的访问限制是到root就结束);访问结束过后,p++,p = 3;然后第二个A开始去看有没有和p = 3的字符串匹配的,发现并没有,然后这个时候,开始访问第二个A的fail,(这里要注意的是,不管当前节点的子节点是否能和主字符串中对应的位置的字符相匹配,也要照常访问当前这个节点的fail),第二个A的fail指向第一个A,这个A的子节点(也就是第二个A)表示的字符正好和p = 3的字符相匹配,而且是一个子字符串的末尾,这个时候+1,然后继续从第一个A开始访问fail,访问结束过后,p++,p = 4,然后开始去看第二个A的子节点有没有和p = 4匹配的,显然是有的,而且是一个子字符串的末尾,这个时候个数+1,然后仍然开始访问fail,访问结束和发现当前的Trie已经访问到底部了,就重新倒回到root,继续向下找,当p = 7的时候,才发现root的子节点有能和p = 7匹配的,就是中间的G,之后的过程和上面的过程差不多,我就直接跳到p = 9的时候,p = 9的时候,Trie匹配的位置是第三行的F,是一个子字符串的末尾,个数+1,然后开始访问C的fail,发现C的fail所指向的子节点也是F,并且也是一个子字符串的末尾,然后个数+1;差不多就这么一个过程,匹配的例子包含了两种,一种是 AAA匹配子字符串AA的情况,和GCF匹配子字符串GCF,CF的情况;这些弄懂了的话,这道题就能做了;下面给出代码(是指针版的,个人比较喜欢指针,数组的模版我看不懂) (我这里提醒一下,我是为了让你们比较好理解,图片上面那部分的文字说明和图片下面的是有点出入的,但是以图片下面的为准,上面的如果发现有什么不对的可以直接忽视掉,看图片下面的文字说明)

#include<iostream>
#include<string>
#include<cstring>
#include<cstdio>
#include<queue>using namespace std;#define Maxn 2000005char ptr[1005][55],s[Maxn];
int vis[1005]; // 记录编号的个数 struct Trie {int Ed;Trie *child[95];Trie *fail;Trie () {memset(child,0,sizeof(child));fail = NULL;Ed = 0;}
} *root;void insert_ (char *str,int x) {int index, len = strlen(str);Trie *rt = root;for (int i = 0; i < len; ++i) {index = str[i];if(!rt->child[index]) rt->child[index] = new Trie ();rt = rt->child[index];}rt->Ed = x;  // 在子字符串的末尾标记编号
}void getfail () {Trie *rt = root,*p;queue<Trie *> qu;qu.push(rt);while (!qu.empty()) {rt = qu.front(); qu.pop();for (int i = 0; i < 95; ++i) {if(rt->child[i]) {if(rt == root) rt->child[i]->fail = root;else {p = rt->fail;while (p) {if(p->child[i]) {rt->child[i]->fail = p->child[i];break;}else p = p->fail;}if(!p) rt->child[i]->fail = root;}qu.push(rt->child[i]);}}}
}void query (char *str) {int index, len = strlen(str);Trie *rt = root, *p;for (int i = 0; i < len; ++i) {index = str[i];if(index >= 95) continue;while (!rt->child[index] && rt != root) rt = rt->fail;rt = rt->child[index];rt = (rt == NULL) ? root : rt;p = rt;while (p != root && p->Ed >= 0) {vis[p->Ed]++; // 遇到编号就++ p = p->fail;}}
}void refree (Trie *rt) {for (int i = 0; i < 95; ++i) {if(rt->child[i]) refree(rt->child[i]);delete rt->child[i];}
}int main (void)
{int n;while (scanf("%d",&n) != EOF) {root = new Trie();for (int i = 1; i <= n; ++i) { scanf(" %s",ptr[i]); insert_ (ptr[i],i); }memset(vis,0,sizeof(vis));getchar();gets(s);getfail();query (s);for (int i = 1; i <= n; ++i) {if(vis[i] > 0) {printf("%s: %d\n",ptr[i],vis[i]);}}refree(root);}return 0;
}

HDU 3065 病毒侵袭持续中 【AC自动机模版题】相关推荐

  1. HDU 3065病毒侵袭持续中 AC自动机

    题意很明了,就是找每个匹配串在文本中出现的次数,并且根据题意可以可以有重复部分. 所以这个题与板子不同的地方就是查找的一部分. 还有就是多组输入!!! /*┆ ┏┓ ┏┓ ┆┆┏┛┻━━━━━━┛┻┓ ...

  2. HDU - 3065 病毒侵袭持续中(AC自动机)

    题目链接:点击查看 题目大意:给出 n 个模式串和一个文本串,问每个模式串在文本串中分别出现了多少次 题目分析:虽然暴跳fail也是可以实现这个题目的,但个人感觉更好的方法还是建立fail树后在树上d ...

  3. hdu 3065 病毒侵袭持续中(AC自动机)

    病毒侵袭持续中                                                                     Time Limit: 2000/1000 MS ...

  4. HDU-3065 病毒侵袭持续中 AC自动机又是一板子!

    病毒侵袭持续中 上一题是求出现多少病毒输出病毒序号,而这题输出每个病毒出现的次数.这题有字典树基础都能做出来,把叶子节点用相应的编号标记起来,匹配的时候遍历到叶子节点用一个数组把次数存起来就行了. 有 ...

  5. HDU 3065 病毒侵袭持续中(AC自动机)题解

    题意:要你找到主串中每个模式串的个数. 思路:题目都没说是多组数据,结果没while(~)直接WA了,和上一题差不多,可以用map或者开个数组储存.指针要记得回收内存,不然MLE. #include& ...

  6. BNUOJ 7178 病毒侵袭持续中

    病毒侵袭持续中 Time Limit: 1000ms Memory Limit: 32768KB This problem will be judged on HDU. Original ID: 30 ...

  7. 病毒侵袭持续中(HDU-3065)

    Problem Description 小t非常感谢大家帮忙解决了他的上一个问题.然而病毒侵袭持续中.在小t的不懈努力下,他发现了网路中的"万恶之源".这是一个庞大的病毒网站,他有 ...

  8. HDU2896(AC自动机模版题)

    AC自动机模版题: 方法一:超时 #include<iostream> #include<algorithm> #include<cstring> #include ...

  9. HDU2222(AC自动机模版题)

    AC自动机是Trie树和KMP的结合物,但是其实KMP在这里体现了思想,而Trie树才是最重要的,要想学懂AC自动机,学习Trie树是必须的,这些是自己在学习AC自动机的个人看法,我也是在网上学习了大 ...

  10. HDU 2896病毒侵袭

    当太阳的光辉逐渐被月亮遮蔽,世界失去了光明,大地迎来最黑暗的时刻....在这样的时刻,人们却异常兴奋--我们能在有生之年看到500年一遇的世界奇观,那是多么幸福的事儿啊~~  但网路上总有那么些网站, ...

最新文章

  1. 教你用netstat-实践案例
  2. C# 各版本更新简介
  3. 清华博士教你如何用推荐算法技术「找到女朋友」
  4. SQL Server创建Job, 实现执行相同脚本而产生不同作业计划的探究
  5. 永恒之蓝(MS17-010)补丁KB号
  6. Android学习笔记(十一)——将Fragment添加到Activity中以及参数传递
  7. 坚决不要使用SQL逻辑删除本番环境的数据,降低程序风险
  8. 纯python好找工作吗_python现在还好找工作吗?
  9. 京东首页链接的商品竟然下柜?
  10. mysql批量执行语句_mysql批量执行sql语句
  11. CAN总线介绍及硬件设计
  12. 线性代数 --- 用内积重新定义矩阵的转置(个人学习笔记)
  13. android 自定义ImageView实现图片手势滑动 多点触摸放大缩小效果
  14. -ile “……的“ 形容词后缀
  15. 【旅行】飘过江南(一)。
  16. 下雨天,走一段路,是走淋雨少还是跑
  17. Jm 18.4 MVC 报告一
  18. SQL Server中修改表的前缀
  19. 服装内部条码和服装国标码的区别
  20. 简单Java小程序----有界面ATM机

热门文章

  1. matlab矩阵特征值分解,矩阵特征值分解与奇异值分解含义解析及应用
  2. 后端学习 Java笔记(附源码)
  3. Python3爬取搜狗微信公众号
  4. arduino 鸿蒙,arduino入门开发案例(上)
  5. 单车架的ANSYS有限元分析
  6. Word添加脚注自定义标记
  7. android switch的使用方法,Android UI控件Switch的使用方法
  8. 复活吧,我的僵尸路由器们,wrt1041n v2再战江湖,路由器硬改硬刷手把手超详细教学
  9. word如何让单页变横向
  10. 慕课网上socket课程的学习