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

 

转载于:https://www.cnblogs.com/Philip-Tell-Truth/p/5183309.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Based on the following story, continue the story by writing two paragraphs, paragraph 1 beginning with "A few weeks later, I went to the farm again. " and paragraph 2 beginning with "I was just about to leave when the hummingbird appeared."respectively with 150 words. I was invited to a cookout on an old friend's farm in western Washington. I parked my car outside the farm and walked past a milking house which had apparently not been used in many years.A noise at a window caught my attention,so I entered it. It was a hummingbird,desperately trying to escape. She was covered in spider-webs and was barely able to move her wings. She ceased her struggle the instant I picked her up. With the bird in my cupped hand, I looked around to see how she had gotten in. The broken window glass was the likely answer. I stuffed a piece of cloth into the hole and took her outside,closing the door securely behind me. When I opened my hand, the bird did not fly away; she sat looking at me with her bright eyes.I removed the sticky spider-webs that covered her head and wings. Still, she made no attempt to fly.Perhaps she had been struggling against the window too long and was too tired? Or too thirsty? As I carried her up the blackberry-lined path toward my car where I kept a water bottle, she began to move. I stopped, and she soon took wing but did not immediately fly away. Hovering,she approached within six inches of my face. For a very long moment,this tiny creature looked into my eyes, turning her head from side to side. Then she flew quickly out of sight. During the cookout, I told my hosts about the hummingbird incident. They promised to fix the window. As I was departing, my friends walked me to my car. I was standing by the car when a hummingbird flew to the center of our group and began hovering. She turned from person to person until she came to me. She again looked directly into my eyes, then let out a squeaking call and was gone. For a moment, all were speechless. Then someone said, “She must have come to say good-bye.”
02-12

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值