奶牛矩阵

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

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

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

  那怎么找呢?既然是二维的,每一行和每一列都看成是一个新的“元素”(注意这里是以列和行为单位的,而不是以单个元素为单位,如果这题以单个元素为单位会出现严重的错误,这是网上很多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. 全屏背景:15个jQuery插件实现全屏背景图像或媒体

    动态网站通常利用背景图像或预加载屏幕,以保证所有资源都加载到页面上,在浏览器中充分呈现.现在很多网站都炫耀自己的图像作为背景图像全屏背景,追溯到旧的Flash网站却用自己的方式在HTML资源重布局. ...

  2. Eclipse中使用tomcat 8服务器初级教程

    Eclipse中使用tomcat容器时,经常遇到的问题是启动不成功,输入localhost:8080报404,本文就是教大家破解这个问题.(不过这是很初级的问题了,大牛勿喷) 步骤 1 Window- ...

  3. mysql 总结二(自定义存储过程)

    mysql执行流程: sql命令--->mysql引擎-----(分析)---->语法正确-----(编译)--->可识别命令----(执行)---->执行结果---(返回)- ...

  4. lua操作json,mysql,redis等

    ==========================example for lua json======================= local cjson = require("cj ...

  5. C/C++多种方法获取文件大小(转)

    源码下载:点击下载 源码如下: #include <iostream> #include <io.h> #include <sys\stat.h> #include ...

  6. MySQL Python教程(3)

    Class cursor.MySQLCursor 具体方法和属性如下:Constructor cursor.MySQLCursorMethod MySQLCursor.callproc(procnam ...

  7. iOS企业级开发初级课程-UIView与控件(20集)

    UIView与控件向大家介绍了视图和控件之间的关系以及应用画面的建构层次.然后是对标签.按钮.文本框.文本视图.开关.滑块.分段控件.网页控件.屏幕滚动控件.等待控件.进度条.警告.动作选单.工具栏. ...

  8. NSRunLoop 概述和原理

    NSRunLoop 概述和原理 1.什么是NSRunLoop? 我们会经常看到这样的代码: - (IBAction)start:(id)sender { pageStillLoading = YES; ...

  9. php写入txt换行符

    1.问题 写入txt文件想换行,老是直接输出了\r\n. 2.解决 要用双引号对\r\n进行解释,否则php会直接当字符输出. 3.例子 要求:往test.txt文本每一行后面加abc $a=file ...

  10. 正则表达式30分钟入门:http://deerchao.net/tutorials/regex/regex.htm#mission

    http://deerchao.net/tutorials/regex/regex.htm#mission