POJ 2185 Milking Grid

题目大意:

给一个R * C的字符矩阵, 找一个最小面积的矩阵, 能用它覆盖整个矩阵(所谓覆盖, 就是用这个小矩阵能拼凑出一个大矩阵包含原矩阵, 且左上角重合).

 

简要分析:

在草稿纸上画了一下后发现行和列是独立的, 可以分开做. 若第i行的字符串最小能被长为Li的字符串覆盖, 那么答案矩阵最小的列数就是各行Li的的最小公倍数.

于是问题转化成了, 对每一行如何求Li. 设某一行字符串为s1s2..sM, L = Li, N = kL, 则字符串可以写成s1s2..sLs1s2..sL..s1s2..sLs(N + 1)s(N + 2)..sM的形式, 注意这时从1开始的后缀与从L + 1开始的后缀有长为M - L的最长公共前缀, 且从L + 1开始的后缀可以匹配到末尾! 于是pre[M] = M - L, pre就是KMP算法中的那个数组(也叫next), 那么我们的答案便是L = M - pre[M]. 这样为什么能使L最小呢? 废话嘛, KMP算法保证了pre[M]最大, M是定值, 那么L自然就最小了.

PS: pre[i]表示在i + 1位匹配失败后, 往前应该到的地方, 显然s[1..pre[i]] = s[i - pre[i] + 1..i], 于是上面的那个结论就能证明了, 吧...

 

代码实现:

View Code
 1 #include <cstdio>
2 #include <cstdlib>
3 #include <cstring>
4 #include <algorithm>
5 using namespace std;
6
7 const int MAX_N = 10000, MAX_M = 75;
8 int n, m;
9 char buf[MAX_N + 1][MAX_M + 2];
10 int pre[MAX_N + 1];
11 char tmp[MAX_N + 1];
12
13 int km(int sz) {
14 pre[1] = 0;
15 int j = 0;
16 for (int i = 2; i <= sz; i ++) {
17 while (j && tmp[j + 1] != tmp[i]) j = pre[j];
18 if (tmp[j + 1] == tmp[i]) j ++;
19 pre[i] = j;
20 }
21 return sz - pre[sz];
22 }
23
24 int gcd(int a, int b) {
25 return b ? gcd(b, a % b) : a;
26 }
27
28 int lcm(int a, int b) {
29 return a / gcd(a, b) * b;
30 }
31
32 int main() {
33 scanf("%d%d", &n, &m);
34 for (int i = 1; i <= n; i ++) scanf("%s", buf[i] + 1);
35 int r = 1, c = 1;
36 for (int i = 1; i <= n; i ++) {
37 for (int j = 1; j <= m; j ++) tmp[j] = buf[i][j];
38 c = lcm(c, km(m));
39 if (c > m) {
40 c = m;
41 break;
42 }
43 }
44 for (int i = 1; i <= m; i ++) {
45 for (int j = 1; j <= n; j ++) tmp[j] = buf[j][i];
46 r = lcm(r, km(n));
47 if (r > n) {
48 r = n;
49 break;
50 }
51 }
52 printf("%d\n", r * c);
53 return 0;
54 }
posted @ 2012-03-10 20:22  zcwwzdjn  阅读(604)  评论(0编辑  收藏  举报