奶牛阵列
Time Limit:10000MS Memory Limit:65536K
Total Submit:26 Accepted:18
Case Time Limit:1000MS
Description
每天早晨约翰的奶牛都会在挤奶的时候排成阵列,即站成R(1<=R<=10000)行C(1<=C<=75)列的矩阵。我们知道,约翰是奶牛专家,他打算写一本关于喂养奶牛的书,他发现,当奶牛按不同血统标记以后,整个大矩阵就像由很多小矩阵无缝拼接的一样。
请帮助约翰找到面积最小的模型矩阵,使他能拼出整个大矩阵,当然,模型矩阵的尺寸不一定能整除大矩阵,也就是说你可以用若干个模型矩阵,拼出一个包含大矩阵的更大的矩阵。
Input
第一行, 两个整数R和C
接下来是由大写字母构成的R*C的矩阵
Output
一个整数,表示最小模型矩阵的面积。
Sample Input
2 5
ABABA
BABAB
Sample Output
4
Hint
样例说明:
模型矩阵如下:
AB
BA
拼出的大矩阵如下:
ABABAB
BABABA
Source
usaco 2003 fall Milking Grid
=============================================================================================
分析:
开始拿到这道题的时候我也没什么思路,就想着写暴力骗点分,因为根本没复习,然后看了第3天我还是觉得想
这道比较好写。于是我就去翻了下KMP的讲义,然后发现一个很牛逼的结论:
Fail[]的性质:
1.如果Len%(Len-Fail[len-1])==0则字符串中必存在最小循环节
2.该循环节长度为Len-Fail[len-1]
3.循环次数为Len/(Len-Fail[len-1])
但是这道题是二维的,感觉这个结论还是没啥用= =
然后我就看样例
ABABAB 此时的矩阵为: AB
BABABA BA
他的规模是2*2,然后我就惊奇的发现2是行的最小循环结,也是列的最小循环结(现在看来貌似很明显= =)
然后我就想把它行的循环结*列的循环结不就是答案了么
再来看组数据吧:
我们将每一行看成一个字符
ABAA '#'
ABAA --> '#' -->所以此时的行的循环结长度为1 -->旋转下,再求一次,就得到列的循环结
ABAA '#'
ABAA '#'
-->ABAB-->A -->此时的最小的循环结长度为3 -->最终得到的是1×3的矩阵ABA
B
A
B
==============================================================================================
放代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#define maxn 10005
using namespace std;
char T[maxn][80],rT[80][maxn];
int Fail[maxn];
void _get_x(int l)
{
int j=-1;
Fail[0]=-1;
for (int i=1;i<l;i++)
{
while(j>-1&&strcmp(T[j+1],T[i]))j=Fail[j];
if(!strcmp(T[j+1],T[i]))j++;
Fail[i]=j;
}
}
void _rotate(int x,int y)
{
for(int i=0;i<y;i++)
{
for(int j=0;j<x;j++)rT[i][j]=T[j][i];
rT[i][x]='\0';
}
}
void _get_y(int l)
{
int j=-1;
Fail[0]=-1;
for(int i=1;i<l;i++)
{
while(j>-1&&strcmp(rT[j+1],rT[i]))j=Fail[j];
if(!strcmp(rT[j+1],rT[i]))j++;
Fail[i]=j;
}
}
int main()
{
//freopen("grid.in","r",stdin);
//freopen("grid.out","w",stdout);
int x,y;
scanf("%d%d",&x,&y);
for(int i=0;i<x;i++)scanf("%s",T[i]);
_get_x(x);//求行的Fail[]
//for(int i=0;i<x;i++)cout<<Fail[i]<<" ";
//putchar(10);
int ans=x-Fail[x-1]-1;//行的最小循环结
_rotate(ans,y); //旋转矩阵
_get_y(y);//求列的Fail[]
//for(int i=0;i<y;i++)cout<<Fail[i]<<" ";
//putchar(10);
ans=ans*(y-Fail[y-1]-1);//列的循环结
printf("%d\n",ans);
return 0;
}