HDU 5164Matching on Array(AC自动机)

这是BC上的一道题,当时比赛没有做,回头看看题解,说是AC自动机,想着没有写过AC自动机,于是便试着抄抄白书的模板,硬是搞了我数个小时2000ms时限1800过了= = !

这里就直接贴上BC的结题报告(#27):http://bestcoder.hdu.edu.cn/solutions.php

1003 Matching on Array

首先我们考虑m=1的情况。给定两个数组A={a1,a2,…,an}和B={b1,b2,…,bk},问B在A中出现了几次。令ci=ai+1ai,1≤i<n,同样令di=bi+1bi,1≤i<k,那么上述问题可以转化为ci和di的模式匹配问题,这个正确性显然,也没有什么好证明的。于是对于m=1的情况只有用个kmp即可搞定。

现在考虑m>1的情况,我们考虑用ac自动机来做。考虑到字符集并不是普通的数字,而是一个分数,我们不放搞个分数类,然后用map存转移边。用m个模式串(Bob的序列)建好自动机之后,把文本串(Alice的序列)在自动机上跑一遍即可统计出答案。

然后是我的代码:

  1 #include <map>
  2 #include <set>
  3 #include <stack>
  4 #include <queue>
  5 #include <cmath>
  6 #include <ctime>
  7 #include <vector>
  8 #include <cstdio>
  9 #include <cctype>
 10 #include <cstring>
 11 #include <cstdlib>
 12 #include <iostream>
 13 #include <algorithm>
 14 using namespace std;
 15 #define INF 1e9
 16 #define inf (-((LL)1<<40))
 17 #define lson k<<1, L, mid
 18 #define rson k<<1|1, mid+1, R
 19 #define mem0(a) memset(a,0,sizeof(a))
 20 #define mem1(a) memset(a,-1,sizeof(a))
 21 #define mem(a, b) memset(a, b, sizeof(a))
 22 #define FOPENIN(IN) freopen(IN, "r", stdin)
 23 #define FOPENOUT(OUT) freopen(OUT, "w", stdout)
 24 #define rep(i, a, b) for(int i = a; i <= b; i ++)
 25 template<class T> T CMP_MIN(T a, T b) { return a < b; }
 26 template<class T> T CMP_MAX(T a, T b) { return a > b; }
 27 template<class T> T MAX(T a, T b) { return a > b ? a : b; }
 28 template<class T> T MIN(T a, T b) { return a < b ? a : b; }
 29 template<class T> T GCD(T a, T b) { return b ? GCD(b, a%b) : a; }
 30 template<class T> T LCM(T a, T b) { return a / GCD(a,b) * b;    }
 31
 32 typedef __int64 LL;
 33 //typedef long long LL;
 34 const int MAXN = 110000;
 35 const int MAXM = 1000006;
 36 const double eps = 1e-10;
 37 const LL MOD = 1000000007;
 38
 39 int T, n, m, s, k, x, y;
 40 //val[p]用来存放以节点p结尾的字典树中共有多少个模式串(包含在失配链中的模式串)
 41 //f是失配函数, last用于记录失配链上的上一个结点(不是节点, 具体见白书, 这里last数组可以删除)
 42 int val[MAXM], f[MAXM], last[MAXM];
 43 LL ans;
 44
 45 struct Node {//自定义分数类型, 并重载几个必要的运算符
 46     int up, down;//分子及分母
 47     Node(){}
 48     Node(int _up, int _down) {
 49         int g = GCD(_up, _down);
 50         up = _up / g;
 51         down = _down / g;
 52     }
 53     bool operator == (const Node& A) const {
 54         return up == A.up && down == A.down;
 55     }
 56     bool operator != (const Node& A) const {
 57         return up != A.up || down != A.down;
 58     }
 59     bool operator <  (const Node& A) const {
 60         if(up != A.up) return up < A.up;
 61         return down < A.down;
 62     }
 63 };
 64
 65 map<int, map<Node, int> >ch;//字典树链
 66 vector<Node>e[MAXM];//邻接表存边
 67 Node fa[MAXN], chi[MAXN];//记录查找串(father)以及模式串(child)
 68
 69 void init()//初始化
 70 {
 71     s = 1;  ans = 0; //s是字典树的大小
 72     ch.clear(); e[0].clear();
 73     mem0(val); mem0(f); mem0(last);
 74 }
 75
 76 void input(int n, Node* a)//输入
 77 {
 78     scanf("%d", &x);
 79     for(int i = 1; i < n; i ++) {
 80         scanf("%d", &y);
 81         a[i - 1] = Node(x, y);
 82         x = y;
 83     }
 84 }
 85
 86 void addEdge()//想字典树上插入一条链,目前val[p]存放的是到达结点p的相同的链的条数
 87 {
 88     int p = 0;
 89     for(int i = 0; i < k - 1; i ++) {
 90         Node u = chi[i];
 91         if(ch[p][u] == 0) {//第二维是Node类型
 92             e[p].push_back(u);//记录边
 93             val[s] = 0;
 94             e[s].clear();//扩展出一条新边之前,把边清空
 95             ch[p][u] = s++;
 96         }
 97         p = ch[p][u];
 98     }
 99     val[p] ++;//说明之前有这样一条链,条数++
100 }
101
102 void getFail()//拿到字典树上的失配函数,与白书几乎一致,只是改成了邻接链表存边
103 {
104     queue<int>q;
105     for(int i = 0; i < e[0].size(); i ++) {
106         int u = ch[0][e[0][i]];
107         f[u] = last[u] = 0;
108         q.push(u);
109     }
110     while(!q.empty()) {
111         int r = q.front(); q.pop();
112         for(int i = 0; i < e[r].size(); i ++) {
113             Node c = e[r][i];
114             int u = ch[r][c];
115             if(!u) {
116                 ch[r][c] = ch[f[r]][c];
117                 continue;
118             }
119             q.push(u);
120             int v = f[r];
121             while(v && !ch[v][c]) v = f[v];
122             f[u] = ch[v][c];
123             last[u] = val[f[u]] ? f[u] : last[f[u]];
124             val[u] += val[last[u]];//加上了一句,表示以u结尾的链的条数
125         }
126     }
127 }
128
129 void getNext()
130 {
131     f[0] = f[1] = 0;
132     for(int i = 1; i < k - 1; i ++) {
133         int j = f[i];
134         while(j && chi[i] != chi[j]) j = f[j];
135         f[i + 1] = chi[i] == chi[j] ? j + 1 : 0;
136     }
137 }
138
139 LL KMP()
140 {
141     if(k == 1) return ans;
142     getNext();
143     int j = 0;
144     for(int i = 0; i < n - 1; i ++) {
145         while(j && chi[j]!=fa[i]) j = f[j];
146         if(chi[j] == fa[i]) j ++;
147         if(j == k - 1) ans ++;
148     }
149     return ans;
150 }
151
152 //void calc(int j)
153 //{
154 //    if(j) {
155 //        ans += val[j];
156 //        calc(last[j]);
157 //    }
158 //}
159
160 LL ACT()//AC自动机查找过程
161 {
162     getFail();
163     int j = 0;
164     for(int i = 0; i < n - 1; i ++) {
165         Node c = fa[i];
166         while(j && !ch[j][c]) j = f[j];//失配链往回匹配
167         j = ch[j][c];
168         ans += val[j];//加上条数
169         //if(last[j]) calc(last[j]);
170     }
171     return ans;
172 }
173
174 int main()
175 {
176     //FOPENIN("in.txt");
177     //FOPENOUT("out.txt");
178     while(~scanf("%d", &T)) while(T--) {
179         scanf("%d %d", &n, &m);
180         init();
181         input(n, fa);//输入查找串
182         for(int i = 1; i <= m; i ++) {
183             scanf("%d", &k);
184             input(k, chi);
185             if(k > 1) addEdge();
186             else ans += n;//由于k=1时只有一个数直接加上n即可
187         }
188         cout<< (m == 1 ? KMP() : ACT()) << endl;//输出答案
189     }
190     return 0;
191 }
时间: 2024-10-26 21:10:29

HDU 5164Matching on Array(AC自动机)的相关文章

HDU 2896 病毒侵袭 AC自动机题解

本题是在text里面查找key word的增强版,因为这里有多个text. 那么就不可以简单把Trie的叶子标志记录修改成-1进行加速了,可以使用其他技术,我直接使用个vis数组记录已经访问过的节点,达到加速效果,速度还算挺快的. 不过看discuss里面有人直接使用Trie,做出了140ms的速度,而且他的程序严格来说并不正确,可见本题的数据很水啊.Trie的时间效率肯定比AC自动机低,但是在数据很水的特殊情况下,Trie的速度也可以很快的. 注意两个细节: 1 病毒也需要安装顺序输出,不小心

hdu 2896 病毒侵袭 AC自动机(查找包含哪些子串)

病毒侵袭 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 19465    Accepted Submission(s): 4814 Problem Description 当太阳的光辉逐渐被月亮遮蔽,世界失去了光明,大地迎来最黑暗的时刻....在这样的时刻,人们却异常兴奋——我们能在有生之年看到500年一遇的世界奇观,那是多么幸福的事儿

HDU 2222 Keyword Search AC自动机模板

#include <algorithm> #include <iostream> #include <cstring> #include <cstdlib> #include <cstdio> #include <queue> #include <cmath> #include <stack> #include <map> #include <ctime> #include <io

hdu 2825 Wireless Password(ac自动机&amp;dp)

Wireless Password Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 4022    Accepted Submission(s): 1196 Problem Description Liyuan lives in a old apartment. One day, he suddenly found that there

HDU 2222 Keywords Search AC自动机入门题

单词统计的题目,给出一些单词,统计有多少单词在一个文本中出现,最经典的入门题了. AC自动机的基础: 1 Trie, 以这个数据结构为基础的,不过增加一个fail指针和构造fail的函数 2 KMP,不是直接运用KMP,而是需要KMP的思想,KMP思想都没有的话,理解这个算法会更加吃力的. 注意本题的单词会有重复出现的,一个单词只能统计一次. 搜索了一下网上的题解,发现好多代码都是一大抄的啊,⊙﹏⊙b汗. 本博客的乃是原创代码,代码风格也是差不多固定的,转载请注明出处:http://blog.c

hdu 3247 Resource Archiver(AC自动机+BFS+DP)

题目链接:hdu 3247 Resource Archiver 题目大意:给定N个需要包含的串,M个不能包含的串,问说满足的最短字符串长度. 解题思路:直接对所有串建立AC自动机,不能满足的串用同一种标记即可.然后处理出所有属于需要包含串的单词节 点,用BFS处理出两两之间的距离,并且过程中是不能经过禁止节点.这样做的原因是节点的个数很多,如果对所有的 节点进行dp的话空间都不够.剩下的就是dp了. #include <cstdio> #include <cstring> #inc

hdu 2825 Wireless Password(AC自动机+状压DP)

题目链接:hdu 2825 Wireless Password 题目大意:N,M,K,M个字符串作为关键码集合,现在要求长度为N,包含K个以上的关键码的字符串有多少个. 解题思路:AC自动机+dp,滚动数组,因为关键码个数不会超过10个,所以我们用二进制数表示匹配的状态.dp[i][j][k] 表示到第i个位置,j节点,匹配k个字符串. #include <cstdio> #include <cstring> #include <queue> #include <

HDU 2457 DNA repair AC自动机 + dp

http://acm.hdu.edu.cn/showproblem.php?pid=2457 首先把病毒串保存一下,然后对于每一个trie上的节点,跑一发AC自动机,建立一个trie图. 建立的时候,对应做一些修改. 比如,现在建立成了这个样子. 如果he是一个病毒串,那么应该相对应的,把she那个he的位置,标志上,它也是病毒串,也就是不能转移到这一个状态. 这个可以在buildfail的时候对应修改. dp, 设dp[i][j],表示处理到字符串的第i个,走到了AC自动机的第j个节点,变成了

HDU 5880 Family View (AC自动机)

Family View Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Description Steam is a digital distribution platform developed by Valve Corporation offering digital rights management (DRM), multiplayer gaming and socia