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

                  

                  奶牛矩阵

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

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

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

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

    abab

    baba

    cdcd

    dcdc

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

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

    ab

    ba

    cd

    dc

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

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

  

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <functional>
 4 #include <string.h>
 5
 6 using namespace std;
 7
 8 typedef char * _String;
 9 void SearchMatch(const int, _String);
10 void Get_Next(const int, const int);
11
12 static char grid[10010][80], tmp[80];
13 static int _Next[10010], cir_match[10010];
14
15 int main(void)//穷举法(500+ms)
16 {
17     int line, colum, min_newmatrix_col, i;
18     while (~scanf("%d%d", &line, &colum))
19     {
20         memset(cir_match, 0, sizeof(cir_match));
21         for (i = 0; i < line; i++)
22         {
23             scanf("%s", grid[i]);
24             strcpy(tmp, grid[i]);
25             SearchMatch(colum, grid[i]);
26         }
27         for (i = 0; i < colum; i++)
28             if (cir_match[i] == line)
29                 break;
30
31         min_newmatrix_col = i;
32         Get_Next(line, min_newmatrix_col);
33         printf("%d\n", min_newmatrix_col*(line - _Next[line]));
34         //colum - _Next[colum]就是关于行的循环节最小长度
35     }
36     return EXIT_SUCCESS;
37 }
38
39 void SearchMatch(const int length, _String match_line)
40 {
41     int pos1, pos2;
42     //cir_match是统计每一行的非循环节的所有值,全部枚举找出最大的即可
43     for (int j = length - 1; j > 0; j--)
44     {
45         //枚举所有循环节长度
46         //理论上j==Length是不用统计的,因为如果不存在比len的最小循环节,那么最小的非循环节只能是长度为len
47         tmp[j] = ‘\0‘;
48         for (pos1 = 0, pos2 = 0; match_line[pos2] != ‘\0‘; pos1++, pos2++)
49         {
50             if (tmp[pos1] == ‘\0‘)
51                 pos1 = 0;//到达指定循环节长度,返回循环节开始
52             if (tmp[pos1] != match_line[pos2])
53                 break;
54         }
55         if (match_line[pos2] == ‘\0‘)
56             cir_match[j]++;
57     }
58 }
59
60 void Get_Next(const int line, const int new_col)
61 {
62     int i = 0, k = -1, j;
63
64     for (j = 0; j < line; j++)
65         grid[j][new_col] = ‘\0‘;//锁定新的矩阵
66     _Next[0] = -1;
67
68     while (i < line)
69     {
70         if (k == -1 || strcmp(grid[i], grid[k]) == 0)
71         {
72             i++;
73             k++;
74             _Next[i] = k;
75         }
76         else k = _Next[k];
77     }
78 }

  

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

  我们这个时候来看一下网上很多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太慢了,有没有更快的方法呢?显然是有的,那就是直接按照循环节的方法把每一列都看成是新的元素就好了

  

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <functional>
 4 #include <string.h>
 5
 6 using namespace std;
 7 typedef char *_String;
 8 static char str[10010][80];
 9 static int _Next[10010];
10
11 int Get_Next_Line(const int);
12 int Get_Next_Colum(const int, const int);
13 bool If_Colum_Match(const int, const int, const int);
14
15 int main(void)//kmp双循环节算法(70+ms)
16 {
17     int line, colum, ans_h, ans_w;
18     while (~scanf("%d%d", &line, &colum))
19     {
20         for (int i = 0; i < line; i++)
21             scanf("%s", str[i]);
22         ans_h = Get_Next_Line(line);
23         ans_w = Get_Next_Colum(line, colum);
24         printf("%d\n", ans_h*ans_w);
25     }
26     return EXIT_SUCCESS;
27 }
28
29 int Get_Next_Line(const int line)
30 {
31     int i = 0, k = -1;
32     _Next[0] = -1;
33
34     while (i < line)
35     {
36         if (k == -1 || strcmp(str[i], str[k]) == 0)
37         {
38             i++;
39             k++;
40             _Next[i] = k;
41         }
42         else k = _Next[k];
43     }
44     return line - _Next[line];
45 }
46
47 int Get_Next_Colum(const int line, const int colum)
48 {
49     int i = 0, k = -1;
50     _Next[0] = -1;
51
52     while (i < colum)
53     {
54         if (k == -1 || If_Colum_Match(line, i, k))
55         {
56             i++;
57             k++;
58             _Next[i] = k;
59         }
60         else k = _Next[k];
61     }
62     return colum - _Next[colum];
63 }
64
65 bool If_Colum_Match(const int line_max, const int pos1, const int pos2)
66 {
67     for (int i = 0; i < line_max; i++)
68         if (str[i][pos1] != str[i][pos2])
69             return false;
70     return true;
71 }

  

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

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

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

时间: 2024-10-24 06:39:03

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

POJ 2185 Milking Grid (二维KMP next数组)

Milking Grid Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 6665   Accepted: 2824 Description Every morning when they are milked, the Farmer John's cows form a rectangular grid that is R (1 <= R <= 10,000) rows by C (1 <= C <= 75

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

Milking Grid Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 6169   Accepted: 2573 Description Every morning when they are milked, the Farmer John's cows form a rectangular grid that is R (1 <= R <= 10,000) rows by C (1 <= C <= 75

POJ 2185 二维KMP

题意:就是让你求出最小的字符矩阵面积,这个矩阵是这个大矩阵里能够循环的,但是并不一定是全部循环相同的,部分相同也算循环,比如样例. 思路:这题挺好的,以前没有想到二维字符串数组也可以用next数组求出其循环节,现在这题正好补了这个空. 解法:把每一个字符串当做字符进行求next数组,然后求出最小的循环字符串长度,即:len-next[len].因为以前求循环节是len/(len-next[len]),括号里面的不就是最小的循环长度嘛! 因为要求这个循环矩阵的长和宽,所以长就是每一行作为一个字符串

POJ2019:二维ST算法解决静态方阵最值问题

我们其实是很有必要把ST算法拓展到二维的,因为二维的RMQ问题还是不少的 int N,B,K; int mm[505]; int val[maxn][maxn]; int dpmin[maxn][maxn][8][8]; int dpmax[maxn][maxn][8][8]; 这里的N是方阵的长宽,此处是正方形题目,然后mm是预处理出来的,方便计算指数 dpmin和dpmax就是预处理数组了 然后看一下开局预处理: void initRMQ(int n,int m) { for(int i=1

写一个二维数组排序算法函数,能够具有通用性,可以调用php内置函数

下面代码没有认真看: <?php //二维数组排序, $arr是数据,$keys是排序的健值,$order是排序规则,1是升序,0是降序 function array_sort($arr, $keys, $order = 0) { if (!is_array($arr)) { return false; } $keysvalue = array(); foreach ($arr as $key => $val) { $keysvalue[$key] = $val[$keys]; } if ($

心上人足球小将射门广告二维码算法

二维码,足球小将二维码如何生成?如何打开?广告平台如何开发? 1.冒泡排序 function bubbleSort(arr){ var i = 0, j = 0; for(i=1; i<arr.length; i++){ for(j=0; j<=arr.length-i; j++){ var temp = 0; // ">" 从小到大排序 // "<" 从大到小排序 if(arr[j] > arr[j+1]){ temp = arr[j

字符串模式匹配算法之二:KMP算法

KMP算法简介 KMP算法全称叫做Knuth-Morris-Pratt Algorithm. 被搜索的字符串称为主串,待搜索的字符串称为模式串. 我们知道朴素模式匹配算法:http://blog.csdn.net/chfe007/article/details/43448655是很低效的,KMP算法从模式串出发,发现模式串中隐藏的信息来减少比较的次数,具体如何做到的可以移步这个链接:http://kb.cnblogs.com/page/176818/ KMP算法的关键在于next数组值的推导.

机器学习真的可以起作用吗?(2)(以二维PLA算法为例)

一个问题:大多数情况下,M(hypothesis set的大小)是无穷大的,例如PLA算法.那么是不是我们的原则1就不能使用了? 我们试着做一些努力: Step1:寻找hypothesis set的effective number来代替M 什么意思呢?就是之前推导中,但是呢,例如在PLA算法中,h1和h2是如此的相像(考虑平面上的直线),所以,如果D对于h1是GOOD,那么对于h2也是GOOD.即:重叠部分太多,我们over-estimatinng了. 现在我们换一种思路.从DataSet的角度

第四十二课 KMP算法的应用

思考: replace图解: 程序完善: DTString.h: 1 #ifndef DTSTRING_H 2 #define DTSTRING_H 3 4 #include "Object.h" 5 6 namespace DTLib 7 { 8 9 class String : Object 10 { 11 protected: 12 char* m_str; 13 int m_length; 14 15 void init(const char* s); 16 bool equa