题目大意:求一篇论文中每个单词分别在论文中出现多少次。

本题用AC自动机太慢,应该用Fail树将AC自动机中所有的Fail指针反向得到一个新树,这就是Fail树。对长度为x的字符串a和长度为y的字符串b,如果a是b的子串,则a可能与位于b[0,a],b[0,a+1],b[0,a+2]...b[0,y]中的后缀相等。根据fail指针的定义,只要沿着反向Fail边走,走到的节点所代表的字符串必然存在与a(前缀)相等的后缀。因此,一遍DFS,返回加上子节点的总Cnt值的当前节点的Cnt值,即可。注意,Trie树中,有些节点是多个字符串公用的,因此每次构造Trie树时,都要对每个节点的Cnt++,以等价于此处存在多个字符串。

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <cassert>
  4. #include <algorithm>
  5. #include <cmath>
  6. #include <queue>
  7. #include <vector>
  8. using namespace std;
  9.  
  10. const int MAX_CHAR = , MAX_LEN = 1e6 + , MAX_STR = ;
  11.  
  12. struct FailTree
  13. {
  14. #define Root _nodes[0]
  15. #define Org(x) x - 'a'
  16.  
  17. struct Node;
  18. struct Edge;
  19.  
  20. struct Node
  21. {
  22. Node *Next[MAX_CHAR], *Fail;
  23. int Cnt;
  24. Edge *Head;
  25. Node() :Cnt(), Fail(NULL), Head(NULL) { memset(Next, NULL, sizeof(Next)); }
  26. };
  27. vector<Node*> _nodes, Tail;
  28.  
  29. struct Edge
  30. {
  31. Node *To;
  32. Edge *Next;
  33. Edge(Node *to, Edge *next):To(to),Next(next){}
  34. };
  35. vector<Edge*> _edges;
  36.  
  37. FailTree()
  38. {
  39. _nodes.push_back(new Node());
  40. }
  41.  
  42. void AddEdge(Node *from, Node *to)
  43. {
  44. Edge *e = new Edge(to, from->Head);
  45. from->Head = e;
  46. _edges.push_back(e);
  47. }
  48.  
  49. Node *BuildTrie(char *s)
  50. {
  51. int len = strlen(s);
  52. Node *cur = Root;
  53. for (int i = ; i < len; i++)
  54. {
  55. if (!cur->Next[Org(s[i])])
  56. _nodes.push_back(cur->Next[Org(s[i])] = new Node());
  57. cur = cur->Next[Org(s[i])];
  58. cur->Cnt++;
  59. }
  60. return cur;
  61. }
  62.  
  63. void Insert(char *s)
  64. {
  65. Tail.push_back(BuildTrie(s));
  66. }
  67.  
  68. void SetFail()
  69. {
  70. static queue<Node*> q;
  71. q.push(Root);
  72. while (!q.empty())
  73. {
  74. Node *cur = q.front();
  75. q.pop();
  76. for (int i = ; i < MAX_CHAR; i++)
  77. {
  78. if (cur->Next[i])
  79. {
  80. Node *temp = cur->Fail;
  81. while (temp)
  82. {
  83. if (temp->Next[i])
  84. {
  85. cur->Next[i]->Fail = temp->Next[i];
  86. AddEdge(temp->Next[i], cur->Next[i]);
  87. break;
  88. }
  89. temp = temp->Fail;
  90. }
  91. if (!temp)
  92. {
  93. cur->Next[i]->Fail = Root;
  94. AddEdge(Root, cur->Next[i]);
  95. }
  96. q.push(cur->Next[i]);
  97. }
  98. }
  99. }
  100. }
  101.  
  102. int Dfs(Node *u)
  103. {
  104. for (Edge *e = u->Head; e; e = e->Next)
  105. u->Cnt += Dfs(e->To);
  106. return u->Cnt;
  107. }
  108. }g;
  109.  
  110. int main()
  111. {
  112. #ifdef _DEBUG
  113. freopen("c:\\noi\\source\\input.txt", "r", stdin);
  114. #endif
  115. int tot;
  116. char s[MAX_LEN];
  117. scanf("%d", &tot);
  118. for(int i=; i<tot; i++)
  119. {
  120. scanf("%s", s);
  121. g.Insert(s);
  122. }
  123. g.SetFail();
  124. g.Dfs(g.Root);
  125. for (int i = ; i < tot; i++)
  126. printf("%d\n", g.Tail[i]->Cnt);
  127. return ;
  128. }

或者不用反向Fail指针也可以,站在后缀上去找其所包含的前缀。这样编程复杂度低一些。

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <vector>
  4. #include <queue>
  5. #include <cassert>
  6. #include <cmath>
  7. #include <algorithm>
  8. using namespace std;
  9.  
  10. const int MAX_CHAR = , MAX_NODE = 5e5 + , MAX_LEN = 1e6 + ;
  11.  
  12. struct Node
  13. {
  14. int Sum, Id, Cnt;
  15. Node *Fail;
  16. Node *Next[MAX_CHAR];
  17. }Nodes[MAX_NODE];
  18. int Nodes_Cnt = ;
  19. char P[MAX_LEN];
  20. Node *WordNode[MAX_NODE];
  21.  
  22. int Ord(char c)
  23. {
  24. return c - 'a';
  25. }
  26.  
  27. Node *NewNode()
  28. {
  29. return ++Nodes_Cnt + Nodes;
  30. }
  31.  
  32. Node *Root()
  33. {
  34. return Nodes + ;
  35. }
  36.  
  37. void BuildTrie(char *s, int id)
  38. {
  39. Node *cur = Root();
  40. int len = strlen(s);
  41. for (int i = ; i < len; i++)
  42. {
  43. if (cur->Next[Ord(s[i])])
  44. cur = cur->Next[Ord(s[i])];
  45. else
  46. cur = cur->Next[Ord(s[i])] = NewNode();
  47. }
  48. cur->Sum++;
  49. cur->Id = id;
  50. WordNode[id] = cur;
  51. }
  52.  
  53. void SetFail()
  54. {
  55. queue<Node*> q;
  56. q.push(Root());
  57. while (!q.empty())
  58. {
  59. Node *cur = q.front();
  60. q.pop();
  61. for (int i = ; i < MAX_CHAR; i++)
  62. {
  63. if (cur->Next[i])
  64. {
  65. Node *temp = cur->Fail;
  66. while (temp)
  67. {
  68. if (temp->Next[i])
  69. {
  70. cur->Next[i]->Fail = temp->Next[i];
  71. break;
  72. }
  73. temp = temp->Fail;
  74. }
  75. if (!temp)
  76. {
  77. cur->Next[i]->Fail = Root();
  78. }
  79. q.push(cur->Next[i]);
  80. }
  81. }
  82. }
  83. }
  84.  
  85. int Dfs1(Node *cur)
  86. {
  87. int cnt = cur->Sum;
  88. for (int i = ; i < MAX_CHAR; i++)
  89. if (cur->Next[i])
  90. cnt += Dfs1(cur->Next[i]);
  91. for (Node *temp = cur; temp != Root(); temp = temp->Fail)
  92. if (temp->Sum)
  93. temp->Cnt+=cnt;
  94. //cur->Cnt += cnt;
  95. return cnt;
  96. }
  97.  
  98. int main()
  99. {
  100. //freopen("c:\\noi\\source\\input.txt", "r", stdin);
  101. int totP;
  102. scanf("%d", &totP);
  103. for (int i = ; i < totP; i++)
  104. {
  105. scanf("%s", P);
  106. BuildTrie(P, i);
  107. }
  108. SetFail();
  109. Dfs1(Root());
  110. for (int i = ; i < totP; i++)
  111. printf("%d\n", WordNode[i]->Cnt);
  112. return ;
  113. }

BZOJ3172 单词 Fail树的更多相关文章

  1. 【BZOJ2905】背单词 fail树+DFS序+线段树

    [BZOJ2905]背单词 Description 给定一张包含N个单词的表,每个单词有个价值W.要求从中选出一个子序列使得其中的每个单词是后一个单词的子串,最大化子序列中W的和. Input 第一行 ...

  2. bzoj 3172: [Tjoi2013]单词 fail树

    题目大意: 一篇论文是由许多单词组成,现在想知道每个单词分别在论文中出现多少次. 题解: 我们首先考虑fail指针的含义 如果fail[x] = y,那么我们就知道y作为x的后缀在x中出现了一次 所以 ...

  3. BZOJ3172[Tjoi2013]单词——AC自动机(fail树)

    题目描述 某人读论文,一篇论文是由许多单词组成.但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次. 输入 第一个一个整数N,表示有多少个单词,接下来N行每行一个单词.每个 ...

  4. BZOJ3172 & 洛谷3966 [Tjoi2013]单词 【fail树】

    3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 4293  Solved: 2083 [Submit][Stat ...

  5. [Bzoj3172][Tjoi2013]单词(fail树)

    3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 4777  Solved: 2345[Submit][Status ...

  6. BZOJ 3172: [Tjoi2013]单词 [AC自动机 Fail树]

    3172: [Tjoi2013]单词 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 3198  Solved: 1532[Submit][Status ...

  7. bzoj 3172 [Tjoi2013]单词(fail树,DP)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3172 [题意] 题目的意思是这样的,给若干个单词,求每个单词在这一堆单词中的出现次数. ...

  8. BZOJ2905: 背单词 AC自动机+fail树+线段树

    $zjq$神犇一眼看出$AC$自动机 $Orz$ 直接就讲做法了 首先对每个串建出$AC$自动机 将$fail$树找到 然后求出$dfs$序 我们发现一个单词 $S_i$是$S_j$的子串当且仅当$S ...

  9. 【洛谷】3966:[TJOI2013]单词【AC自动机】【fail树】

    P3966 [TJOI2013]单词 题目描述 小张最近在忙毕设,所以一直在读论文.一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次. 输入输出 ...

随机推荐

  1. Linux学习笔记--文件夹结构

    暂时先上一张图学习吧,先大致了解好,再进行深入的学习.

  2. 关于如何将_variant_t型转化为int型和string类型

    1)将_variant_t型转化为int型 关于将_variant_t型转化为int型,网上有好多好多参考,但好多都很复杂并且还不对,其实整个转化过程就只一行代码可以搞定: _variant_t a; ...

  3. CSS元素水平垂直居中的方法

    1.  元素水平居中 1.1  设置父元素的属性 text-align: center; 说明:此属性只针对父元素的子元素为内联元素时有效,比如:img,input,select,button等(行内 ...

  4. Android6.0以上版本获取本机蓝牙地址

    Android6.0以上版本使用BluetoothAdapter.getDefaultAdapter().getAddress()是获取不到正确的蓝牙地址的,返回的值都是02:00:00:00:00: ...

  5. Java code List Map, HashMap, JSON parser snippet

    package com.newegg.ec.solr.eventsalestoreservice.tuple; import kafka.message.MessageAndMetadata; pub ...

  6. 【SQL】字符型函数

    1. ASCII ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统. 1) 返回 ...

  7. java解析注解的简单例子

    代码是根据慕课网的教程写的. 自定义类的注解: package com.immoc.test; import java.lang.annotation.Documented; import java. ...

  8. Pull-up resistors

    1 Introduction Pull-up resistors are very common in microcontrollers or any digital logic device. Wi ...

  9. String类面试坑题

    1.面试坑题F:\SHJT\JavaWorkspace\JavaSE\workspace\day13ezra\src\cn\itcast\sh\classcode\BTStringLastIndexO ...

  10. IronPython中共享的C#基类如何向下转型

    在项目中,我们使用IronPython来定义工作流脚本来以应对科研多变的需求.项目使用的主要语言仍然是C#,使用C#封装了各种基础服务与基础设施.Python脚本只使用C#提供的服务,或者说只定义了逻 ...