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},问BA中出现了几次。令ci=ai+1ai,1i<n,同样令di=bi+1bi,1i<k,那么上述问题可以转化为cidi的模式匹配问题,这个正确性显然,也没有什么好证明的。于是对于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 }

 

posted @ 2015-02-02 00:44  再见~雨泉  阅读(336)  评论(0编辑  收藏  举报