UVa 1676 - GRE Words Revenge

contents

  1. 1. Problem
  2. 2. Sample Input
  3. 3. Sample Output
  4. 4. Solution

Problem

操作有二種,學習單詞以及給一篇文章,詢問學習單詞出現總次數。

輸入有經過加密,其加密的位移量為上一次詢問答案。

Sample Input

1
2
3
4
5
6
7
8
9
2
3
+01
+01
?01001
3
+01
?010
?011

Sample Output

1
2
3
4
5
Case #1:
2
Case #2:
1
0

Solution

如果是靜態,則可以想到 AC 自動機自然可以解決,但是 AC 自動機卻不支持動態建造。

為了滿足操作,可以利用快取記憶體的方式,進行分層,將小台 AC 自動機不斷重新建造,當小台 AC 自動機的節點總數大於某個值時,將其與大台 AC 自動機合併。合併操作必須是線性的,不要去重新插入每一個單詞。

特別小心有重複的單詞出現的小台或者是大台的操作,如果有重複必須忽略。雖然在理論上,分兩層操作的總複雜度高於倍增分層 (2, 4, 6, 8, …),但是實作後,倍增分層一直 TLE。

然而這一題還有一種更困難的作法,詳見劉汝佳那本書,估計是一個動態後綴樹自動機 …

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <queue>
#include <map>
#define MAXCHAR 2
#define MAXS (5000005)
#define MAXNODE 526288
#pragma comment( linker, "/STACK:1024000000,1024000000")
using namespace std;
class ACmachine {
public:
struct Node {
Node *next[MAXCHAR], *fail;
int cnt, val;
void init() {
fail = NULL;
cnt = val = 0;
memset(next, 0, sizeof(next));
}
} nodes[MAXNODE];
Node *root;
int size;
Node* getNode() {
Node *p = &nodes[size++];
p->init();
return p;
}
void init() {
size = 0;
root = getNode();
}
int toIndex(char c) {
return c - '0';
}
void dfs(Node *p, Node *q) {
for (int i = 0; i < MAXCHAR; i++) {
if (q->next[i]) {
Node *u = p->next[i], *v = q->next[i];
if (u == NULL)
p->next[i] = getNode(), u = p->next[i];
u->cnt |= v->cnt;
dfs(u, v);
}
}
}
void merge(const ACmachine &other) {
dfs(root, other.root);
}
void insert(const char str[]) {
Node *p = root;
for (int i = 0, idx; str[i]; i++) {
idx = toIndex(str[i]);
if (p->next[idx] == NULL)
p->next[idx] = getNode();
p = p->next[idx];
}
p->cnt = 1;
}
int find(const char str[]) {
Node *p = root;
for (int i = 0, idx; str[i]; i++) {
idx = toIndex(str[i]);
if (p->next[idx] == NULL)
p->next[idx] = getNode();
p = p->next[idx];
}
return p->cnt;
}
void build() { // AC automation
queue<Node*> Q;
Node *u, *p;
Q.push(root), root->fail = NULL;
while (!Q.empty()) {
u = Q.front(), Q.pop();
for (int i = 0; i < MAXCHAR; i++) {
if (u->next[i] == NULL)
continue;
Q.push(u->next[i]);
p = u->fail;
while (p != NULL && p->next[i] == NULL)
p = p->fail;
if (p == NULL)
u->next[i]->fail = root;
else
u->next[i]->fail = p->next[i];
u->next[i]->val = u->next[i]->fail->val + u->next[i]->cnt;
}
}
}
int query(const char str[]) {
Node *u = root, *p;
int matched = 0;
for (int i = 0, idx; str[i]; i++) {
idx = toIndex(str[i]);
while (u->next[idx] == NULL && u != root)
u = u->fail;
u = u->next[idx];
u = (u == NULL) ? root : u;
p = u;
matched += p->val;
}
return matched;
}
void free() {
return ;
// owner memory pool version
queue<Node*> Q;
Q.push(root);
Node *u;
while (!Q.empty()) {
u = Q.front(), Q.pop();
for (int i = 0; i < MAXCHAR; i++) {
if (u->next[i] != NULL) {
Q.push(u->next[i]);
}
}
delete u;
}
}
};
char s[MAXS];
void decodeString(char s[], int L) {
static char buf[MAXS];
int n = (int) strlen(s);
L %= n;
for (int i = L, j = 0; i < n; i++, j++)
buf[j] = s[i];
for (int i = 0, j = n - L; i < L; i++, j++)
buf[j] = s[i];
for (int i = 0; i < n; i++)
s[i] = buf[i];
}
ACmachine cache, disk;
int main() {
int testcase, cases = 0;
int N;
scanf("%d", &testcase);
while (testcase--) {
scanf("%d", &N);
printf("Case #%d:\n", ++cases);
int L = 0; // encrypted key
int cFlag = 0, dFlag = 0;
cache.init(), disk.init();
for (int i = 0; i < N; i++) {
scanf("%s", s);
decodeString(s+1, L);
// printf("%s\n", s);
#define THRESHOLD 4096
if (s[0] == '+') {
if (disk.find(s+1) || cache.find(s+1))
continue;
cache.insert(s+1), cFlag = 0;
if (cache.size > THRESHOLD) {
disk.merge(cache), dFlag = 0;
cache.free();
cache.init(), cFlag = 0;
}
} else {
if (!cFlag) cache.build();
if (!dFlag) disk.build();
int ret = disk.query(s+1) + cache.query(s+1);
printf("%d\n", ret);
L = ret;
}
}
disk.free(), cache.free();
}
return 0;
}
/*
99999
10
+01
+110
?010
+110
+00
+0
?001001
?001001
+110110
?1101001101
6
+01
+110
+110
+00
+0
?001001
20
+101001011
?110100
+11010100
?0011001101
+111011
?00010011
+0111010110
+0000101
+0
+11000
?1
+1010101
+0001
+0110
+0111101111
?1100
+0111
+1001
?0110111011
?1010010100
10
+00
?010110100
+0100000100
+111
+000000
?0000110
+110
+00
+0011
?101001
5
+0
+1000100
+01
+0
?1110010011
2
3
+01
+01
?01001
3
+01
?010
?011
*/