奶牛矩阵

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

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

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

  那怎么找呢?既然是二维的,每一行和每一列都看成是一个新的“元素”(注意这里是以列和行为单位的,而不是以单个元素为单位,如果这题以单个元素为单位会出现严重的错误,这是网上很多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. winServer2003除默认端口外的其他端口只能本地访问,关闭防火墙即可

    winServer2003除默认端口外的其他端口只能本地访问,关闭防火墙即可

  2. POJ 3252 Round Numbers

     组合数学...(每做一题都是这么艰难) Round Numbers Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 7607 A ...

  3. 动态修改attr里的多个属性

    要点: 1.js将字符串转化为object方法,通过新建函数. 2.通过ajax返回的数据是object类型. 3.jquery.attr()里的attr是object类型 例子:主要实现后台返回的a ...

  4. CentOS 6.5 zabbix 3.0.4 监控MySQL性能

    安装mysql [root@test3 /]# yum -y install mysql mysql-server 初始化数据库 [root@test3 /]# /etc/init.d/mysqld ...

  5. CSS使用自定义光标样式-遁地龙卷风

    测试环境是chrome浏览器 Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357. ...

  6. php实现图片缩放功能类

    http://www.poluoluo.com/jzxy/201312/255447.html <?php /** * Images类是一个图片处理类 * @package applicatio ...

  7. Android手机的上网功能需要用到APN(网络接入点)的设置 电信

    手机apn出问题了,上不网 电信天翼: 我们经常使用的APN有三个,分别是NET网络设置.WAP网络设置和彩信网络设置. 1.NET网络设置 名称:CTNET APN:#777 用户名:ctnet@m ...

  8. Dijkstra(歪果仁的名字真是长。。。)

    Dijkstra算法又称为单源最短路径,所谓单源是在一个有向图中,从一个顶点出发,求该顶点至所有可到达顶点的最短路径问题.       设G=(V,E)是一个有向图,V表示顶点,E表示边.它的每一条边 ...

  9. Fedora 25 Alpha版本今天发布啦

    时隔Fedora 24发布后的3个月,Fedora项目团队非常开心的宣布任何感兴趣的用户都能下载和测试即将到来的Fedora 25操作系统的Alpha预发布版本,在Fedora 25 Alpha里程碑 ...

  10. div 加滚动条

    div 加滚动条的方法: <div style="position:absolute; height:400px; overflow:auto"></div> ...