奶牛矩阵

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

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

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

  那怎么找呢?既然是二维的,每一行和每一列都看成是一个新的“元素”(注意这里是以列和行为单位的,而不是以单个元素为单位,如果这题以单个元素为单位会出现严重的错误,这是网上很多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. 数字格式化函数:Highcharts.numberFormat()

    (转)数字格式化函数:Highcharts.numberFormat() 一.函数说明 该函数用于图表中数值的格式化,常见用途有数值精度控制.小数点符.千位符显示控制等.   二.函数使用   1.函 ...

  2. vmstat、top

    vmstat是一个查看虚拟内存(Virtual Memory)使用状况的工具,使用vmstat命令可以得到关于进程.内存.内存分页.堵塞IO.traps及CPU活动的信息. vmstat 最常用的有两 ...

  3. 详细介绍如何在win7下首次实现通过Git bash向Github提交项目

    详细介绍如何在win7下首次实现通过Git bash向Github提交项目 引自:http://jingpin.jikexueyuan.com/article/35944.html 作者: wddoe ...

  4. [译]Mongoose指南 - Schema

    定义schema 用mongoose的第一件事情就应该是定义schema. schema是什么呢? 它类似于关系数据库的表结构. var mongoose = require('mongoose'); ...

  5. Mac Pro 开机自启动 PHP-FPM,Nginx,MySql 等软件

    在Mac下安装好了PHP开发环境(PHP-FPM,Nginx,MySql), 想设置成开机自启动,原来以为和一般的Linux系统一样,也是在rc.d这样目录放置启动脚本.在网上查了一些资料,发现苹果应 ...

  6. LESS介绍及其与Sass的差异

    自从一个月前我偶然发现LESS之后我就开始坚定的使用它了.CSS本身对我来说从来不是问题,但是我很好奇使用变量来沿着一个调色盘为我的网站或模板创建一些东西的想法.拥有一个提供固定数量选项可选的色盘可以 ...

  7. 9.4用WebApi去连接外部认证服务

    原文链接:http://www.asp.net/web-api/overview/security/external-authentication-services VS2013和Asp.Net4.5 ...

  8. Java File 常用操作回顾

    最近项目中要用到File这个类,温故而知新,回过头来回顾下这个File类,File类主要是对磁盘目录,文件进行操作的Api,具体其实查JDK api 的File全能获取到. 下面写一些文件目录的基本操 ...

  9. 使用 Github Pages 发布你的项目文档

    导读 你可能比较熟悉如何用 Github Pages 来分享你的工作,又或许你看过一堂教你建立你的第一个 Github Pages 网站的教程.近期 Github Pages 的改进使得从不同的数据源 ...

  10. 微软“One Windows”的梦想已经破灭了吗?

    导读 Windows 10 正式公布的时候,微软曾表示该系统将开启更为个性化的计算新纪元,可让用户在使用各类设备处理各项事务时,享受到一致.熟悉和可兼容的体验,从 Xbox 到 PC 和手机,再到平板 ...