poj2185之KMP应用

Milking Grid
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 4804 Accepted: 1999

Description

Every morning when they are milked, the Farmer John's cows form a rectangular grid that is R (1 <= R <= 10,000) rows by C (1 <= C <= 75) columns. As we all know, Farmer John is quite the expert on cow behavior, and is currently writing a book about feeding behavior in cows. He notices that if each cow is labeled with an uppercase letter indicating its breed, the two-dimensional pattern formed by his cows during milking sometimes seems to be made from smaller repeating rectangular patterns. 

Help FJ find the rectangular unit of smallest area that can be repetitively tiled to make up the entire milking grid. Note that the dimensions of the small rectangular unit do not necessarily need to divide evenly the dimensions of the entire milking grid, as indicated in the sample input below. 

Input

* Line 1: Two space-separated integers: R and C 

* Lines 2..R+1: The grid that the cows form, with an uppercase letter denoting each cow's breed. Each of the R input lines has C characters with no space or other intervening character. 

Output

* Line 1: The area of the smallest unit from which the grid is formed 

Sample Input

2 5
ABABA
ABABA

Sample Output

2

Hint

The entire milking grid can be constructed from repetitions of the pattern 'AB'.

支持他们的观点:最小重复矩阵一定靠左上角。
第一步也是找最小重复子矩阵的宽,网上的大部分代码就卡在这里,如实例(poj讨论数据):
实例1:
2 8
ABCDEFAB
AAAABAAA
实例2:
2 8
ABCDEFAB
AAAABABC
误区1:网上的代码找宽的方法(多数代码):用KMP的next求出每行最小重复子串长度,然后求这些长度的公倍数,对于这个实例:第一行为6,第二行为5,6与5的最小公倍数为30,大于8则取8为宽,这种思路对实例2很合适,但明显对于实例1是错误的。(这种思路的AC代码网上有非常多)
误区2:同样用KMP的next求出每行最小重复子串长度,然后取最大的那个长度作为宽,这种对于实例1来说刚好能过,但对于实例2就无能为力了。(这种思路的AC代码网上有比较少)
出现以上误区的博文误倒大家,只能说明POJ数据太弱。
说说我的思路吧:找出每行的重复子串长度的各种可能情况,然后每行都有的并且是最小长度作为宽width。
第二步找最小重复子矩阵的高,这个思路和网上的差不多,取每行的宽为width的前缀作为一个单位,对这0到r-1个单位求出KMP的next函数,找出最小重复子序列的单位数作为高height,最终答案为width*height。

给出两组数据:
Input

2 8
ABCDEFAB
ABCDEABC
2 8
ABCDEFAB
AAAABAAA

Output

16
12
解释下关于len-next[len]为什么是循环周期:


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#define INF 99999999
using namespace std;

const int MAX=10000+10;
char s[MAX][77];
int rnum[77],next[MAX];

int get_next(int n){
	int i=-1,j=0;
	next[0]=-1;
	while(j<n){
		if(i == -1 || strcmp(s[i],s[j]) == 0)next[++j]=++i;//把每一行看成一个字符求next 
		else i=next[i];
	}
	return n-next[n];
}

int main(){
	int r,w,x,y;
	while(cin>>r>>w){
		memset(rnum,0,sizeof rnum);
		for(int i=0;i<r;++i){
			cin>>s[i];
			for(int j=1;j<=w;++j){
				for(x=0,y=j;y<w;++x,++y){
					if(s[i][x%j] != s[i][y])break;//判断是否是循环周期 
				}
				if(y == w)++rnum[j];//统计所有循环周期长度的个数,如aaaaa就有a,aa,aaa,aaaa,aaaaa多个循环周期 
			}
		}//求y也可以和列一样求法,但是比较每一列字符串却比较难,所以用暴力统计(上面是暴利统计)去求 
		for(y=1;y<=w;++y)if(rnum[y] == r)break;//求所有行共同最小周期y,0~y-1为一个周期 
		for(int i=0;i<r;++i)s[i][y]='\0';
		x=get_next(r);//求0~y列共同最小周期x,0~x-1为一个周期 
		cout<<x*y<<endl;
	}
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值