hdu 2243考研路茫茫——单词情结—解题报告
算法:AC自动机的理解,Trie图的应用。
我的理解:Trie图是建立在AC自动机基础上的,前者比后者有更多的应用。对于AC自动机的理解, 请参考这篇大牛推荐论文,http://www.cs.uku.fi/~kilpelai/BSA05/lectures/slides04.pdf,虽然是英文的,读了一个下午后,确实觉得讲的很明白; 对Trie的理解,请参考王贇的论文http://wenku.baidu.com/view/9df73e3567ec102de2bd89ae.html,写的很好。
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2243
题目描述:给你n个字符串S{s1, s2, ...., sn},只包含小写字母,问你长度不超过L,至少包含其中一个字符串(只有小写字母组成)的单词有多少个。
长度不超过L的单词总数减去不包含S中任意一个字符串的单词数, 即ans = 26^1 + 26^2 + … + 26^L - 所有不包含的。
有因为L最大为2^31,所以此题关键有两点:
求出所有不包含的数目; 快速求出前L次幂的和。
1)求所有不包含的数目:再理解AC自动机和Trie图之后,根据Trie图,我们建立邻接矩阵,我们就可以通过矩阵的前L次幂的和,得到所有不包含的数目。顺便在这里推荐一位大牛写的文章http://www.matrix67.com/blog/archives/276, 会更好的理解矩阵。
2)求出前L次幂的和:这个算法之前没有接触过,这次学习了。因为L最大为2^31,依次用快速幂求出每一项的值,复杂度nlgn, 肯定会Tle, 想了好久也没想出好的方法,只好去参考别人的代码,看后,一种感觉,基础不扎实,利用二分的思想,设和为S(n),
n为偶数时:S(n) = S(n/2)*pow(q,n/2)+S(n/2);
n为奇数时:S(n) = S(n/2)*pow(q,n/2)+S(n/2)+pow(q,n/2)。
根据代码,会更好理解此算法。
有了以上两点,这道题基本上也就没什么关键点了。
有一下几点注意:
(1)题目要求,结果模上2^64,只要结果输出格式为%I64u, 原因我也不知道为什么。。。 (尴尬==)
(2)由于在建立矩阵的时候,要用无符号64位的二维数组,题目描述n<6,我没多想,定义了map[10][10],可以运行,但是每次输入,都会栈溢出,纠结了两天,各种改才发现是申请的内存过大。但是,之前遇到内存过大而爆栈是,都不会运行的,不知道这次为什么。。 总之,此题要严格控制内存。
代码如下,有些长:
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4
5 typedef unsigned __int64 u64;
6
7 const int maxn = 6; // 最多词根数
8 const int maxl = 6; // 词根最长长度
9 const int kind = 26; // 字符集字符数
10
11 typedef struct MATRIX{
12 u64 map[maxn*maxl][maxn*maxl];
13 }MAT;
14 struct node {
15 node* next[kind];
16 node* fail;
17 int id, flag;
18 };
19
20 node* root;
21 node* q[maxn*maxl];
22 MAT I, mat;
23 int ID = 0;
24
25 node* GetNode() {
26
27 node* p;
28
29 p = new node;
30 p->id = ID++;
31 p->flag = 0;
32 p->fail = NULL;
33 memset(p->next, NULL, sizeof(p->next));
34
35 return p;
36 }
37
38 void Insert(char word[]) { // Build Trie
39
40 int len = strlen(word);
41 node* p = root;
42 for (int i = 0; i < len; i++) {
43 int pos = word[i] - 'a';
44 if (p->next[pos] == NULL) {
45 p->next[pos] = GetNode();
46 }
47 p = p->next[pos];
48 }
49 p->flag = 1;
50
51 return ;
52 }
53
54 void BuildACAutomaton() {
55
56 int front, rear;
57 node* p;
58
59 front = rear = 0;
60 q[rear++] = root;
61 while (front < rear) {
62 p = q[front++];
63 for (int i = 0; i < kind; i++) {
64 if (p->next[i] != NULL) {
65 q[rear++] = p->next[i];
66 if (p == root) {
67 p->next[i]->fail = root;
68 } else {
69 node* tmp = p->fail;
70 while (tmp != NULL) {
71 if (tmp->next[i] != NULL) {
72 p->next[i]->fail = tmp->next[i];
73 if (tmp->next[i]->flag) { // 后缀节点为危险节点,当节点同为危险节点
74 p->next[i]->flag = 1;
75 }
76 break;
77 }
78 tmp = tmp->fail;
79 }
80 if (tmp == NULL) {
81 p->next[i]->fail = root;
82 }
83 }
84 }
85 }
86 }
87
88 return ;
89 }
90
91 void BuildGraph() {
92
93 node* p;
94 int front, rear;
95
96 memset(mat.map, 0, sizeof(mat.map));
97 front = rear = 0;
98 q[rear++] = root;
99 while (front < rear) {
100 p = q[front++];
101 if (p->flag) continue;
102 for (int i = 0; i < kind; i++) {
103 if (p->next[i] != NULL) {
104 if (p->next[i]->flag) continue;
105 q[rear++] = p->next[i];
106 mat.map[p->id][p->next[i]->id]++;
107 } else {
108 if (p == root) {
109 p->next[i] = p;
110 mat.map[0][0]++;
111 } else {
112 node* tmp = p->fail;
113 p->next[i] = tmp->next[i];
114 if (!tmp->next[i]->flag) mat.map[p->id][p->next[i]->id]++;
115 }
116 }
117 }
118 }
119
120 return ;
121 }
122
123 void InitI() {
124
125 memset(I.map, 0, sizeof(I.map));
126 for (int i = 0; i < ID; i++) {
127 I.map[i][i] = 1;
128 }
129
130 return ;
131 }
132
133 MAT MatrixAdd(MAT &a, MAT &b) {
134
135 MAT c;
136
137 for (int i = 0; i < ID; i++) {
138 for (int j = 0; j < ID; j++) {
139 c.map[i][j] = a.map[i][j] + b.map[i][j];
140 }
141 }
142
143 return c;
144 }
145
146 MAT MatrixMul(MAT &a, MAT &b) {
147
148 MAT c;
149
150 for (int i = 0; i < ID; i++) {
151 for (int j = 0; j < ID; j++) {
152 c.map[i][j] = 0;
153 for (int k = 0; k < ID; k++) {
154 c.map[i][j] += a.map[i][k] * b.map[k][j];
155 }
156 }
157 }
158
159 return c;
160 }
161
162 MAT MatrixSum(MAT m0, int t) {
163
164 MAT m1, m2;
165 int n = 0, bit[65] = {0};
166
167 m1 = m2 = m0;
168 while (t) {
169 bit[n++] = (t & 1);
170 t >>= 1;
171 }
172 for (int i = n-2; i >= 0; i--) {
173 m1 = MatrixMul(m1, MatrixAdd(m2, I));
174 m2 = MatrixMul(m2, m2);
175 if (bit[i]) {
176 m2 = MatrixMul(m2, m0);
177 m1 = MatrixAdd(m1, m2);
178 }
179 }
180
181 return m1;
182 }
183
184 int main () {
185
186 int n, m;
187
188 while (scanf("%d%d", &n, &m) != EOF) {
189 ID = 0;
190 char word[maxl];
191 root = new node;
192 root = GetNode();
193 for (int i = 0; i < n; i++) {
194 scanf("%s", word);
195 Insert(word);
196 }
197 BuildACAutomaton();
198 BuildGraph();
199 InitI();
200 MAT tmp;
201 memset(tmp.map, 0, sizeof(tmp.map));
202 tmp.map[0][0] = 26;
203 tmp = MatrixSum(tmp, m);
204 mat = MatrixSum(mat, m);
205 u64 ans = tmp.map[0][0];
206 for (int i = 0; i < ID; i++) {
207 ans -= mat.map[0][i];
208 }
209 printf("%I64u\n", ans);
210 }
211
212 return 0;
213 }