超级传送门:http://poj.org/problem?id=2185
这组数据,正解应该是12,即2*6,但求LCM的话就是LCM(5, 6) = 30,然后再比较串长,得到结果8,2*8=16 。
再附上一些典型测试数据:
输出:
这道题网上很多其他的blog上的题解都是错的,由于题目自身数据比较水,便都混过去了。其中一种典型的错误解法是求出每一行/列的最小覆盖长度(ABCAB的最小覆盖长度就是3,即对应ABC),然后LCM,若结果大于串长,则取串长。这是一种典型的错误贪心,诸如:
2 8
ABCDEFAB
AAAABAAA
这组数据,正解应该是12,即2*6,但求LCM的话就是LCM(5, 6) = 30,然后再比较串长,得到结果8,2*8=16 。
正确的思路应该是这样的:
1.先纵向匹配,把每一行看成一个字符,求得可覆盖循环节长度plen。
2.将0~plen-1行矩阵转置,变成c行plen列。
3.将此新矩阵每行看成一个字符进行匹配,得到可覆盖循环节长度revplen。
4.结果应为plen * revplen 。
代码:
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 10100;
const int maxm = 10100;
int P[maxm];
char B[maxm][80];
char revB[80][maxm];
int n, m;
void preprocess(char (*B)[80], int* P, int m = -1)
{
P[0] = -1;
int j = -1;
for (int i = 1; i < m; i++)
{
while (j != -1 && strcmp(B[j + 1], B[i]))
j = P[j];
if (!strcmp(B[j + 1], B[i]))
j++;
P[i] = j;
}
}
void revpreprocess(char (*B)[maxm], int* P, int m = -1)
{
P[0] = -1;
int j = -1;
for (int i = 1; i < m; i++)
{
while (j != -1 && strcmp(B[j + 1], B[i]))
j = P[j];
if (!strcmp(B[j + 1], B[i]))
j++;
P[i] = j;
}
}
void reverse(char (*revB)[maxm], char (*B)[80], int r, int c)
{
for (int i = 0; i < c; i++)
{
for (int j = 0; j < r; j++)
revB[i][j] = B[j][i];
revB[i][r] = '\0';
}
}
int main()
{
int r, c;
while (scanf("%d%d", &r, &c) > 0)
{
for (int i = 0; i < r; i++)
scanf("%s", B[i]);
preprocess(B, P, r);
int plen = r - P[r - 1] - 1;
reverse(revB, B, plen, c);
revpreprocess(revB, P, c);
int revplen = c - P[c - 1] - 1;
printf("%d\n", plen * revplen);
}
return 0;
}
再附上一些典型测试数据:
4 6
ABAABA
ABAABA
ABAABA
ABAABA
2 5
ABABA
ABABA
1 1
A
1 2
AA
1 2
AB
1 5
ABCAB
4 6
ABAABA
ABAABA
ABAABC
ABAABA
2 8
ABCDEFAB
ABCDEABC
2 8
ABCDEFAB
AAAABAAA
4 3
abc
bcd
def
abc
2 2
aa
aa
4 4
ABAB
CDCD
BABA
DCDC
2 8
ACBABACB
ACBABACB
4 1
A
A
B
A
4 4
abca
abva
abxa
abca
3 5
ABCDE
BCDEF
CDEFG
4 5
ABABA
BABAB
ABABA
BABAB
4 5
ABABA
BABAA
ABCCA
BAABA
输出:
3
2
1
1
2
3
18
16
12
9
1
8
5
3
9
15
4
20