清醒疯子 2018-02-04
求一个最小矩阵,经过复制能够覆盖原矩阵(覆盖,不是填充,复制之后可以有多的)
*解法:横着竖着kmp,求最大公倍数的做法是不对的,见http://blog.sina.com.cn/s/blog_69c3f0410100tyjl.html
正解如http://poj.org/showmessage?message_id=153316
求出每一行的可以重复的子串长度,所有行都可以的(公共的)那个最小的长度就是最终所求矩阵的宽
求出宽之后把剩下那些列砍掉,然后要求矩阵的高,把每行看作一个整体kmp即可
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define SZ 10005 int nxt[SZ]; char s[SZ][80], tmp[80]; int r, c, k, cnt[80];//cnt相当于桶,记录每个长度出现多少次,如果一个长度出现r次即表示这个长度在每行都出现过,即为最终矩阵的宽 void get_next(char p[], int plen) { int i = 0, j = -1; nxt[0] = -1; while(i < plen) { if(j == -1 || p[i] == p[j]) { i++; j++; nxt[i] = j; } else j = nxt[j]; } k = c - nxt[plen];//后缀 cnt[k]++; if(k == c) return; for(int i = 0; i < nxt[plen]; i++) tmp[i] = p[i + plen - nxt[plen]];//把后缀拿出来,再kmp get_next(tmp, nxt[plen]); return; } void get_next_str(char p[][80], int plen) { int i = 0, j = -1; nxt[0] = -1; while(i < plen) { if(j == -1 || strcmp(p[i], p[j]) == 0)//把每行看作一个整体 { i++; j++; nxt[i] = j; } else j = nxt[j]; } return; } int main() { scanf("%d %d", &r, &c); for(int i = 0; i < r; i++) scanf(" %s", s[i]); for(int i = 0; i < r; i++) get_next(s[i], c); for(int i = 0; i < c; i++) if(cnt[i] == r) { c = i;//i长度出现了r次,即为最终的宽 break; } for(int i = 0; i < r; i++) s[i][c] = '\0';//把剩下的列砍掉 get_next_str(s, r); printf("%d\n", c * (r - nxt[r])); return 0; }