Match:Milking Grid(二维KMP算法)(POJ 2185)

题目大意:给定一个矩阵,要你找到一个最小的矩阵,这个矩阵的无限扩充的矩阵包含着原来的矩阵
思路:乍一看这一题确实很那做,因为我们不知道最小矩阵的位置,但是仔细一想,如果我们能把矩阵都放在左上角该多好,这样一来这一题好像又是循环数组那个样子了(二维的)。
而事实上我们确实可以把所有情况都放在左上角,因为矩阵里面的元素的相对位置是不变的,这样一来我们就可以把矩阵看成都是一些元素从左上角往右下角扩充。那么现在问题就又回到了循环节的问题上了,我们可以把矩阵看成是很多很多个字符串组成,我们要找的就是一个最小的循环节的面积(一维的循环节是可以找长度,二维的循环节我们找面积)。
那怎么找呢?既然是二维的,每一行和每一列都看成是一个新的“元素”(注意这里是以列和行为单位的,而不是以单个元素为单位,如果这题以单个元素为单位会出现严重的错误,这是网上很多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)的更多相关文章
- POJ 2185 Milking Grid [二维KMP next数组]
传送门 直接转田神的了: Milking Grid Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 6665 Accept ...
- POJ 2185 - Milking Grid (二维KMP)
题意:给出一个字符矩形,问找到一个最小的字符矩形,令它无限复制之后包含原来的矩形. 此题用KMP+枚举来做. 一维的字符串匹配问题可以用KMP来解决.但是二维的就很难下手.我们可以将二维问题转化为一维 ...
- 题解报告:poj 2185 Milking Grid(二维kmp)
Description Every morning when they are milked, the Farmer John's cows form a rectangular grid that ...
- 二维KMP - 求字符矩阵的最小覆盖矩阵 - poj 2185
Milking Grid Problem's Link:http://poj.org/problem?id=2185 Mean: 给你一个n*m的字符矩阵,让你求这个字符矩阵的最小覆盖矩阵,输出这个最 ...
- 【KMP】POJ 2185 Milking Grid -- Next函数的应用
题目链接:http://poj.org/problem?id=2185 题目大意:求一个二维的字符串矩阵的最小覆盖子矩阵,即这个最小覆盖子矩阵在二维空间上不断翻倍后能覆盖原始矩阵. 题目分析:next ...
- POJ--2158--------------Milking Grid(最小覆盖字符矩阵)---(开二维kmp)
Milking Grid Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 6169 Accepted: 2573 Desc ...
- POJ_2185_二维KMP
http://poj.org/problem?id=2185 求最小覆盖矩阵,把KMP扩展到二维,行一次,列一次,取最小覆盖线段相乘即可. #include<iostream> #incl ...
- java几个经典的算法题目----------二维矩阵算法
public class testClockwiseOutput { public static void main(String[] args) { //1.构建矩阵数据 int[][] arr = ...
- POJ2185 Milking Grid KMP两次(二维KMP)较难
http://poj.org/problem?id=2185 大概算是我学KMP简单题以来最废脑子的KMP题目了 , 当然细节并不是那么多 , 还是码起来很舒服的 , 题目中描写的平铺是那种瓷砖一 ...
随机推荐
- MySQL数据库INSERT、UPDATE、DELETE以及REPLACE语句的用法详解
本篇文章是对MySQL数据库INSERT.UPDATE.DELETE以及REPLACE语句的用法进行了详细的分析介绍,需要的朋友参考下 MySQL数据库insert和update语句引:用于操作数 ...
- 基于zepto判断mobile的横竖屏状态
借用jquery mobile中的代码,删除了一些多余的部分,简单的基于zepto的模块 var CheckOrientation = (function(){ var win = $( window ...
- 如何在发布博客时插入复杂公式——Open Live Writer
1.http://latex.codecogs.com/eqneditor/editor.php 2.使用Word发布
- 微信小程序未来怎么样?听微盟卫晓祥来说说
微信小程序宣布公测已经一个多月了,开发者一片火热,未来会怎么样?听微盟卫晓祥来说说.微盟移动营销事业部总经理卫晓祥表示,微信小程序最吸引商户的地方在于:一方面小程序作为一种全新的连接用户与服务的方式, ...
- 跨Controllers传数据
今天遇到两个问题,第一个是跨controller传值,后一个是比较简单的linq数据库查询问题.先描述以下问题我有一个入库单和一个入库明细,入库的逻辑是先填写入库单在填入库明细.两者要么同时完成,要么 ...
- iOS并发编程指南之同步
1.gcd fmdb使用了gcd,它是通过 建立系列化的G-C-D队列 从多线程同时调用调用方法,GCD也会按它接收的块的顺序来执行. fmdb使用的是dispatch_sync,多线程调用a ser ...
- JS中的prototype(原文地址:http://www.cnblogs.com/yjf512/archive/2011/06/03/2071914.html)
JS中的phototype是JS中比较难理解的一个部分 本文基于下面几个知识点: 1 原型法设计模式 在.Net中可以使用clone()来实现原型法 原型法的主要思想是,现在有1个类A,我想要创建一个 ...
- mac os 基本命令
unix 系统命令行 ,仅供参考 目录操作 命令名 功能描述 使用举例 mkdir 创建一个目录 mkdir dirname rmdir 删除一个目录 rmdir dirname ...
- git上传github上
1.git init --初始化git (选择文件夹) 2.git add README --添加项目(项目的文件夹) 3.git commit -m "SSM(360)" ...
- sql执行顺序
SQL 不同于与其他编程语言的最明显特征是处理代码的顺序.在大数编程语言中,代码按编码顺序被处理,但是在SQL语言中,第一个被处理的子句是FROM子句,尽管SELECT语句第一个出现,但是几乎总是最后 ...