这是我之前博客里提到的一道AC自动机的练手题,但是要完成这道题,我之前博客里提到的东西还不够,这里总结一下这道题。

  这道题不是一般的裸的AC自动机,它的询问和插入是交叉出现的所以用我之前写的板子不大合适,这道题在构建自动机时不能改变原有的 Trie树 的结构,所以没有代表字符串的结点的不需要去改它的值,所以我在 build() 函数 处做了一些修改。在复杂度方面,如果是强上普通的AC自动机,最差会达到O(n^2),感觉不太好。我们这里可以用到平方分割的套路,搞大小两个自动机,每次插入都在小自动机上进行,当小自动机里的结点达到一定量(sqrt(n))时,就将两自动机合并,并将小自动机清空。合并的方式也很好理解,就将两个自动机的节点一一对应,若小自动机上的结点在大自动机上没有,就在大自动机上新建结点,并修改它的值。总复杂度大概是这样:O(n*sqrt(n)(小的)+O(sqrt(n)*n)(大的)=O(n*sqrt(n))。其实那个上界也不一定要严格的根号n,自己大概估计一个常数就差不多了。哦对,还有一点,我发现好多板子里在构建新结点时都打了一个 newnode() 函数,包括我自己的板子也是这么打的。这样打其实是很慢的,我在做这道题时就各种 T飞 ,后来把这个去掉搞成其他的方法就快了很多(虽然我也不知道为什么),下面的代码里会体现。

  祝大家切题愉快

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstdlib>
  4. #include<cstring>
  5. #include<algorithm>
  6. #include<queue>
  7. #define maxn (511111)
  8. #define N (6000000)
  9. #define il inline
  10. #define ll long long
  11. #define RG register
  12. using namespace std;
  13. char s[N],ss[N];
  14. struct Trie{
  15. int son[maxn][2],fail[maxn],size,root;
  16. bool val[maxn]; //标记是否出现
  17. il void init(){
  18. size=1; root=0;
  19. memset(son,0,sizeof(son));
  20. memset(val,0,sizeof(val));
  21. memset(fail,0,sizeof(fail));
  22. }
  23. il int idx(char c){ //卡常专用
  24. return c-'0';
  25. }
  26. il int insert(char *s){ //插入新单词
  27. RG int cur=root;
  28. for(RG int i=0;s[i];i++){ //卡常小技巧,不要每次计算len
  29. RG int id=idx(s[i]);
  30. if( !son[cur][id] ) son[cur][id]=size++;
  31. cur=son[cur][id];
  32. }
  33. val[cur]=true;
  34. return cur;
  35. }
  36. il bool in(char *s){ //查询单词是否在自动机内
  37. RG int cur=root;
  38. for(RG int i=0;s[i];i++){
  39. RG int id=idx(s[i]);
  40. if( !son[cur][id] ) return false;
  41. cur=son[cur][id];
  42. }
  43. return val[cur];
  44. }
  45. il void build(){ //构建自动机
  46. queue<int>Q;
  47. for(RG int i=0;i<2;i++)
  48. if( son[root][i] ) //只加入队列,不用修改值
  49. Q.push(son[root][i]);
  50. while(!Q.empty()){
  51. RG int cur=Q.front(); Q.pop();
  52. for(RG int i=0;i<2;i++){
  53. RG int Son=son[cur][i];
  54. if(!Son) continue;
  55. Q.push(Son); //同上
  56. RG int f=fail[cur];
  57. while(f && !son[f][i] ) f=fail[f];
  58. fail[Son]=son[f][i];
  59. }
  60. }
  61. }
  62. il int query(char *s){ //查询次数
  63. RG int cur=root,ans=0;
  64. for(RG int i=0;s[i];i++){
  65. RG int id=idx(s[i]);
  66. while(cur && !son[cur][id] ) cur=fail[cur];
  67. cur=son[cur][id];
  68. RG int k=cur;
  69. while(k){
  70. ans+=val[k];
  71. k=fail[k];
  72. }
  73. }
  74. return ans;
  75. }
  76. }small,big;
  77. il void dfs(RG int u,RG int v){ //用于合并
  78. for(RG int i=0;i<2;i++)
  79. if(small.son[v][i]){
  80. RG int cur2=small.son[v][i];
  81. if( !big.son[u][i] ){
  82. memset(big.son[big.size],0,sizeof(big.son[big.size]));
  83. big.son[u][i]=big.size++; //建立新结点
  84. }
  85. RG int cur1=big.son[u][i];
  86. big.val[cur1] |=small.val[cur2];
  87. dfs(cur1,cur2);
  88. }
  89. }
  90. il void join(){
  91. dfs(0,0);
  92. small.init();
  93. big.build();
  94. }
  95. int main(){
  96. RG int Case,n;
  97. scanf("%d",&Case);
  98. for(RG int k=1;k<=Case;k++){
  99. printf("Case #%d:\n",k);
  100. scanf("%d",&n);
  101. small.init(); big.init();
  102. RG int l=0;
  103. while(n--){
  104. scanf("%s",s);
  105. RG int len=strlen(s+1);
  106. ss[0]=s[0];
  107. for(RG int i=0;i<len;i++)
  108. ss[i+1]=s[ (i+l%len+len)%len+1 ];
  109. ss[len+1]='\0';
  110. if(ss[0]=='+'){
  111.               if( small.in(ss+1) || big.in(ss+1) ) continue;
  112.               //若该单词已经存在就跳过
  113. small.insert(ss+1); small.build();
  114. if( small.size>2333 ) join();
  115. }
  116. else{
  117. l=small.query(ss+1)+big.query(ss+1);
  118. printf("%d\n",l);
  119. }
  120. }
  121. }
  122. return 0;
  123. }

  真是服了这鬼畜的缩进。。。真难看。。。

HDU-4787 GRE Words Revenge 解题报告的更多相关文章

  1. [HDU 4787] GRE Words Revenge (AC自动机)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4787 题目大意: 给你若干个单词,查询一篇文章里出现的单词数.. 就是被我水过去的...暴力重建AC自 ...

  2. ●HDU 4787 GRE Words Revenge

    题链: http://acm.hdu.edu.cn/showproblem.php?pid=4787 题解: AC自动机(强制在线构造) 题目大意: 有两种操作, 一种为:+S,表示增加模式串S, 另 ...

  3. HDU 4787 GRE Words Revenge

    Description Now Coach Pang is preparing for the Graduate Record Examinations as George did in 2011. ...

  4. [HDU4787]GRE Words Revenge 解题报告

    这是我之前博客里提到的一道AC自动机的练手题,但是要完成这道题,我之前博客里提到的东西还不够,这里总结一下这道题. 这道题不是一般的裸的AC自动机,它的询问和插入是交叉出现的所以用我之前写的板子不大合 ...

  5. hdu 1556.Color the ball 解题报告

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1556 题目意思:有 n 个气球从左到右排成一排,编号依次为1,2,3,...,n.给出 n 对 a, ...

  6. hdu 1160 FatMouse's Speed 解题报告

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1160 题目意思:给出一堆老鼠,假设有 n 只(输入n条信息后Ctrl+Z).每只老鼠有对应的weigh ...

  7. hdu 1879 继续畅通工程 解题报告

    题目链接:http://code.hdu.edu.cn/showproblem.php?pid=1879 这条题目我的做法与解决Constructing Roads的解法是相同的. 0 表示没有连通: ...

  8. hdu 1233 还是畅通工程 解题报告

    题目链接:http://code.hdu.edu.cn/showproblem.php?pid=1233 并查集的运用, 实质就是求最小生成树.先对所有的村庄距离从小到大排序,然后判断村庄之间是否属于 ...

  9. hdu 1213 How Many Tables 解题报告

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1213 有关系(直接或间接均可)的人就坐在一张桌子,我们要统计的是最少需要的桌子数. 并查集的入门题,什 ...

随机推荐

  1. [0] Devexpress 控件参数集合

    gridview控件/统计功能 比如对“数量”列进行统计,只要在GridControl的设计器中设置SummaryItem:   SummaryItem.DisplayFormat = "{ ...

  2. 微信支付生成带logo的二维码

    利用到一个qrcode类 比较简洁 原作者没有加入二维码嵌入logo的功能 在这里我进行了小小的修改 可以实现生成微信支付二维码时打上logo 生成png格式的利用到该类中的png方法(我已经改好了) ...

  3. js实现点击copy,可兼容

    在实现功能时写的一个小demo,可以实现点击复制内容(任何你需要copy的文字内容data). 经测试,可兼容 chrome, edge, firefox, ie, opera, safari,至于版 ...

  4. 愚公oracle数据库同步工具

    最近,利用一些时间对oracle数据库实时同步工具做了一些调研分析,主要关注了linkedin的databus和阿里的yugong两个中间件,其中databus需要在每个待同步的表上增加额外的列和触发 ...

  5. SOD开源框架MSF(消息服务框架)进阶篇

    复习:在上一篇我介绍了MSF的基本订阅,模式就是,客户端A,订阅服务器.客户端B,订阅服务器.通过服务器广播消息, 所有订阅过的客户端都能接到消息. 进阶:在上一篇的基础上,增加客户端A,发送信息到服 ...

  6. Java注解--实现简单读取excel

    实现工具类 利用注解实现简单的excel数据读取,利用注解对类的属性和excel中的表头映射,使用Apache的poi就不用在业务代码中涉及row,rows这些属性了. 定义注解: @Retentio ...

  7. 利用 Traceview 精准定位启动时间测试的异常方法 (工具开源)

    机智的防爬虫标识原创博客地址:http://www.cnblogs.com/alexkn/p/7095855.html博客求关注: http://www.cnblogs.com/alexkn 1.启动 ...

  8. mysql 左连接 右连接 内链接

    一般所说的左连接,右连接是指左外连接,右外连接.做个简单的测试你看吧.先说左外连接和右外连接:[TEST1@orcl#16-12月-11] SQL>select * from t1;ID NAM ...

  9. Kafka 源代码分析之Log

    这里分析Log对象本身的源代码. Log类是一个topic分区的基础类.一个topic分区的所有基本管理动作.都在这个对象里完成.类源代码文件为Log.scala.在源代码log目录下. Log类是L ...

  10. LaTeX初识 新手入门 Texlive和Texmaker学习

    转载自:http://blog.sina.com.cn/s/blog_90444ed201016iq6.html http://blog.csdn.net/zb1165048017/article/d ...