奶牛矩阵

  题目大意:给定一个矩阵,要你找到一个最小的矩阵,这个矩阵的无限扩充的矩阵包含着原来的矩阵

  思路:乍一看这一题确实很那做,因为我们不知道最小矩阵的位置,但是仔细一想,如果我们能把矩阵都放在左上角该多好,这样一来这一题好像又是循环数组那个样子了(二维的)。

  而事实上我们确实可以把所有情况都放在左上角,因为矩阵里面的元素的相对位置是不变的,这样一来我们就可以把矩阵看成都是一些元素从左上角往右下角扩充。那么现在问题就又回到了循环节的问题上了,我们可以把矩阵看成是很多很多个字符串组成,我们要找的就是一个最小的循环节的面积(一维的循环节是可以找长度,二维的循环节我们找面积)。

  那怎么找呢?既然是二维的,每一行和每一列都看成是一个新的“元素”(注意这里是以列和行为单位的,而不是以单个元素为单位,如果这题以单个元素为单位会出现严重的错误,这是网上很多AC代码的通病,我们先往下看)。一般的我们可以先这么想,我们可以先固定行,确定最小循环节的列数,然后以这个循环节的列数来找循环节的行数(把每一行都看成是新的元素),比如

    abab

    baba

    cdcd

    dcdc

  我们可以枚举每一行的循环节的长度(注意这里我用的是枚举),列举所有可能的循环情况,找到公共的最短的循环节的列数,比如例子里面所有行的公共的最短循环列数是2

  那么我们就可以在这个矩阵

    ab

    ba

    cd

    dc

  里面找到循环节的行数,把每一行都看成一个元素,那么这个最长的行数是4,最小循环节的面积是4。

  按照这个思路我们可以写出这样的代码

  

 #include <iostream>
#include <algorithm>
#include <functional>
#include <string.h> using namespace std; typedef char * _String;
void SearchMatch(const int, _String);
void Get_Next(const int, const int); static char grid[][], tmp[];
static int _Next[], cir_match[]; int main(void)//穷举法(500+ms)
{
int line, colum, min_newmatrix_col, i;
while (~scanf("%d%d", &line, &colum))
{
memset(cir_match, , sizeof(cir_match));
for (i = ; i < line; i++)
{
scanf("%s", grid[i]);
strcpy(tmp, grid[i]);
SearchMatch(colum, grid[i]);
}
for (i = ; i < colum; i++)
if (cir_match[i] == line)
break; min_newmatrix_col = i;
Get_Next(line, min_newmatrix_col);
printf("%d\n", min_newmatrix_col*(line - _Next[line]));
//colum - _Next[colum]就是关于行的循环节最小长度
}
return EXIT_SUCCESS;
} void SearchMatch(const int length, _String match_line)
{
int pos1, pos2;
//cir_match是统计每一行的非循环节的所有值,全部枚举找出最大的即可
for (int j = length - ; j > ; j--)
{
//枚举所有循环节长度
//理论上j==Length是不用统计的,因为如果不存在比len的最小循环节,那么最小的非循环节只能是长度为len
tmp[j] = '\0';
for (pos1 = , pos2 = ; match_line[pos2] != '\0'; pos1++, pos2++)
{
if (tmp[pos1] == '\0')
pos1 = ;//到达指定循环节长度,返回循环节开始
if (tmp[pos1] != match_line[pos2])
break;
}
if (match_line[pos2] == '\0')
cir_match[j]++;
}
} void Get_Next(const int line, const int new_col)
{
int i = , k = -, j; for (j = ; j < line; j++)
grid[j][new_col] = '\0';//锁定新的矩阵
_Next[] = -; while (i < line)
{
if (k == - || strcmp(grid[i], grid[k]) == )
{
i++;
k++;
_Next[i] = k;
}
else k = _Next[k];
}
}

  

  可见这样的代码的实现是正确的。

  我们这个时候来看一下网上很多AC代码的思路,他们的思路是把每一行的都用一次kmp,算出每一行的最短循环节,然后求他们的lcm(lcm超过原来的列数就把答案设置成最大的列数),对每一列也同样操作,最后的面积就是两个lcm的乘积

  但是这样的代码无法通过下面里的例子: 

    2 8

    ABCDEFAB

    AAAABAAA

  原因是出在行的计算上,对于第一行,计算出来的最大lcm是5,第二行是6,他们的lcm是30,取最大的列数8,对于列算出来的lcm是2,答案是16,显然是错的,因为这个的答案是12。

  其实原因很简单,因为第一行可行的循环不仅有5,还有678,但是kmp把678都忽略了,所以枚举可以解决这个问题。

  回到原问题来,显然枚举要500+ms太慢了,有没有更快的方法呢?显然是有的,那就是直接按照循环节的方法把每一列都看成是新的元素就好了

  

 #include <iostream>
#include <algorithm>
#include <functional>
#include <string.h> using namespace std;
typedef char *_String;
static char str[][];
static int _Next[]; int Get_Next_Line(const int);
int Get_Next_Colum(const int, const int);
bool If_Colum_Match(const int, const int, const int); int main(void)//kmp双循环节算法(70+ms)
{
int line, colum, ans_h, ans_w;
while (~scanf("%d%d", &line, &colum))
{
for (int i = ; i < line; i++)
scanf("%s", str[i]);
ans_h = Get_Next_Line(line);
ans_w = Get_Next_Colum(line, colum);
printf("%d\n", ans_h*ans_w);
}
return EXIT_SUCCESS;
} int Get_Next_Line(const int line)
{
int i = , k = -;
_Next[] = -; while (i < line)
{
if (k == - || strcmp(str[i], str[k]) == )
{
i++;
k++;
_Next[i] = k;
}
else k = _Next[k];
}
return line - _Next[line];
} int Get_Next_Colum(const int line, const int colum)
{
int i = , k = -;
_Next[] = -; while (i < colum)
{
if (k == - || If_Colum_Match(line, i, k))
{
i++;
k++;
_Next[i] = k;
}
else k = _Next[k];
}
return colum - _Next[colum];
} bool If_Colum_Match(const int line_max, const int pos1, const int pos2)
{
for (int i = ; i < line_max; i++)
if (str[i][pos1] != str[i][pos2])
return false;
return true;
}

  

  至此我们已经把二维循环节的问题解决了,这就是网上的所谓降阶法,其实也不是很难理解,一维循环节的问题请戳HDU 3746

  参考:http://blog.csdn.net/u013480600/article/details/22990715

     http://blog.sina.com.cn/s/blog_69c3f0410100tyjl.html

Match:Milking Grid(二维KMP算法)(POJ 2185)的更多相关文章

  1. POJ 2185 Milking Grid [二维KMP next数组]

    传送门 直接转田神的了: Milking Grid Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 6665   Accept ...

  2. POJ 2185 - Milking Grid (二维KMP)

    题意:给出一个字符矩形,问找到一个最小的字符矩形,令它无限复制之后包含原来的矩形. 此题用KMP+枚举来做. 一维的字符串匹配问题可以用KMP来解决.但是二维的就很难下手.我们可以将二维问题转化为一维 ...

  3. 题解报告:poj 2185 Milking Grid(二维kmp)

    Description Every morning when they are milked, the Farmer John's cows form a rectangular grid that ...

  4. 二维KMP - 求字符矩阵的最小覆盖矩阵 - poj 2185

    Milking Grid Problem's Link:http://poj.org/problem?id=2185 Mean: 给你一个n*m的字符矩阵,让你求这个字符矩阵的最小覆盖矩阵,输出这个最 ...

  5. 【KMP】POJ 2185 Milking Grid -- Next函数的应用

    题目链接:http://poj.org/problem?id=2185 题目大意:求一个二维的字符串矩阵的最小覆盖子矩阵,即这个最小覆盖子矩阵在二维空间上不断翻倍后能覆盖原始矩阵. 题目分析:next ...

  6. POJ--2158--------------Milking Grid(最小覆盖字符矩阵)---(开二维kmp)

    Milking Grid Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 6169   Accepted: 2573 Desc ...

  7. POJ_2185_二维KMP

    http://poj.org/problem?id=2185 求最小覆盖矩阵,把KMP扩展到二维,行一次,列一次,取最小覆盖线段相乘即可. #include<iostream> #incl ...

  8. java几个经典的算法题目----------二维矩阵算法

    public class testClockwiseOutput { public static void main(String[] args) { //1.构建矩阵数据 int[][] arr = ...

  9. POJ2185 Milking Grid KMP两次(二维KMP)较难

    http://poj.org/problem?id=2185   大概算是我学KMP简单题以来最废脑子的KMP题目了 , 当然细节并不是那么多 , 还是码起来很舒服的 , 题目中描写的平铺是那种瓷砖一 ...

随机推荐

  1. jdk的安装及配置

    前几天重新了下系统,所以JDK也要重新安装,顺带温故了安装及配置的过程,记录下来.(安装的版本是JDK1.7.0) 后面基本都是点下一步(i第一步选:开发工具),路径我改为E:/java/jdk 1. ...

  2. 编写更好的C#代码

    引言 开发人员总是喜欢就编码规范进行争论,但更重要的是如何能够在项目中自始至终地遵循编码规范,以保证项目代码的一致性.并且团队中的所有人都需要明确编码规范所起到的作用.在这篇文章中,我会介绍一些在我多 ...

  3. 微信要革"传统电视"的命吗?

    除夕夜不知大家是否发现微信摇一摇界面下方的菜单变成4个了?“红包,人,歌曲,电视”,红包和电视是新增的,几天之后红包这个菜单消失了,电视菜单还在,能够摇出一些电视台的直播节目单,以往的摇电视借用的是摇 ...

  4. SQL中关于日期的常用方法

    mysql数据库: logintime >= STR_TO_DATE('$$START_TIME','%Y-%m-%d %H:%i:%s') AND logintime < STR_TO_ ...

  5. 如何解决Mac里面解压后文件名乱码问题

    如果你把Mac当成你的主要工作机器,而你的同事用的都是Windows,有时候交换文档就是一件很痛苦的事,比如今天要说到的问题:当同事传给你一个zip文件,结果你拿过来解压后发现里面有些文件的文件名如果 ...

  6. SQL Server 跨数据库查询

    语句 SELECT * FROM 数据库A.dbo.表A a, 数据库B.dbo.表B b WHERE a.field=b.field "DBO"可以省略 如 SELECT * F ...

  7. 读w3c中文教程对键盘事件解释的感想 -遁地龙卷风

    写这篇博文源于w3c中文教程对键盘事件的解释, onkeydown 某个键盘按键被按下 onkeypress 某个键盘按键被按下并松开 onkeyup 某个键盘按键被松开 可在实践中发现 只注册key ...

  8. CSS3实现背景颜色渐变 摘抄

    一. Webkit浏览器 (1) 第一种写法: background:-webkit-gradient(linear ,10% 10%,100% 100%, color-stop(0.14,rgb(2 ...

  9. 实操UNITY3D接入91SDK安卓版

    原地址:http://bbs.18183.com/thread-149758-1-1.html 本文内容为创建UNITY3D接入91SDK的DEMO的具体操作过程.本人水平有限,UNITY3D与And ...

  10. iOS 简单音乐播放器 界面搭建

    如图搭建一个音乐播放器界面,具备以下几个简单功能: 1,界面协调,整洁. 2,点击播放,控制进度条. 3.三收藏歌曲,点击收藏,心形收藏标志颜色加深. 4,左右按钮,切换歌曲图片和标题. 5,点击中间 ...