Poj2185Milking Grid KMP做法详解

这篇博客介绍了如何使用KMP算法解决Poj2185题目的矩阵循环元问题。不同于其他题目,本题不需要考虑特定条件,而是寻找所有可能的循环子矩阵,并从中找出最小的。算法分析部分提到,可以对每行和每列分别求最小循环元,然后取最大值,最后计算子矩阵的尺寸。暴力解法复杂,而KMP解法利用next数组特性简化了问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Milking Grid
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 9401 Accepted: 4096

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'.

Source


首先在这里说下,本题的每个单个的步骤的思路可以参考Poj1961 Period最小循环元

这道题。

题目分析:看题目可以发现本题不存在输出无循环子矩阵这种情况,或必须大于1这种条件,所以不同于以上那道题,无论多小(多大)都应按照解法解出来。题目我感觉原文解释的也不是很清楚,但意思就是找到最小循环元,只不过这里的循环元是子矩阵。

算法分析:

  1. 暴力解法:本题暴力我觉得更难写,如果不用KMP中next数组的性质的话,需要考虑的事情太多了。
  2. KMP过本题的解法:对于每一行,都可以求出它的最小循环元(这里的循环元就是线性的了,而且注意这里的最小只能保证这一行)。而为了保证整个矩阵,所以应该在每一行最小的值中取最大的。对于每一列,用同样的方法。最后,要得到这个子矩阵的大小,长乘宽就行了。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char str[10010][80]/*next_[10010]*/;
int n,m,next_[10010];
inline int Getrow_Next(int row)
{
	next_[1]=0;
  	for(int i=2,j=0;i<=m;i++)
  	{
  		while(j>0&&str[row][i]!=str[row][j+1])j=next_[j];
  		if(str[row][i]==str[row][j+1])
  			j++;
  		next_[i]=j;
  	}
	/*int p=1;
	for(int i=2;i<=m;i++)
		if(i%(i-next_[i])==0&&i/(i-next_[i])>1)
			ans[p++]=i/(i-next_[i]);
	p-=2;
	while(ans[p]!=0&&p>1)p--;
	return ans[p];*/

	return m-next_[m];
}
inline int Getcol_Next(int col)
{
	next_[1]=0;
  	for(int i=2,j=0;i<=n;i++)
  	{
  		while(j>0&&str[i][col]!=str[j+1][col])j=next_[j];
  		if(str[i][col]==str[j+1][col])
  			j++;
  		next_[i]=j;
  	}
 	/*int p=1;
	for(int i=2;i<=n;i++)
		if(i%(i-next_[i])==0&&i/(i-next_[i])>1)
			ans[p++]=i/(i-next_[i]);
	p-=2;
	while(ans[p]!=0&&p>1)p--;
	return ans[p];*/
	return n-next_[n];
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		scanf("%s",str[i]+1);

	int minr=0,minc=0;
	for(int i=1;i<=n;i++)
	{
		int ans1 = Getrow_Next(i);
		minr=max(minr,ans1);
	}
	
	for(int i=1;i<=m;i++)
	{
		int ans2 = Getcol_Next(i);
		minc=max(minc,ans2);
	}
	printf("%d\n",minc*minr);
	return 0;
}
代码中注释部分:之前考虑完全按照Period那道题想了,不过这道题中n-next[n]直接就是整个行(列)需要的最小循环元(如果next数组不能从前面获得更新,那就需要n-0也就是整个行,即为一整个行循环某个前缀得不到),而并不用考虑前缀中的每一个最小循环元。这也说明了题目分析里的:可能会出现循环得不到整个矩阵,答案就成了整个矩阵的大小。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值