Openjudge 挤奶网格

目录

挤奶网格

要求:

描述:

输入:

输出: 

样例输入:

样例输出:

提示:

 思路分析:

最终代码:


​​​​​​​

挤奶网格

要求:

总时间限制: 3000ms

内存限制: 65536kB

描述:

 每天早上奶牛被挤奶的时候,农夫约翰的奶牛会成一个R行,C列的长方形网格(1 <= R <= 10,000,1 <= C <= 75)。据我们所知,约翰i研究奶牛行为上,是一个专家,同时也在编写一个关于如何饲养奶牛的书。他发现如果将每头奶牛用一个大写字母来标识其种类,在挤奶的时候他的奶牛所形成的二维模式似乎有时候是从一些更小的长方形模式重复得来。帮助 寻找最小面积的长方形单位,该长方形单位可以通过重复从而构成整个挤奶网格,注意到这个小的长方形单位的维度并不需要由整个挤奶网格的维度均分得到,具体可以参见示例。

输入:

第一行: 两个以空格间隔的整数 R和C
第二行到第R+1行:牛形成的网格,每个格子以一个大写字母来表示每个奶牛的种类。这R行中每行包含C个中间没有间隔符的字母。

输出: 

一行,即网格形成所需要的最小单位的面积。 

样例输入:

2 5
ABABA
ABABA

样例输出:

2

提示:

整个挤奶网格可以从模式 'AB'重复得来。第一行和最后一行的A用模式‘AB’的前缀部分A得到。即求最小覆盖矩阵。

 思路分析:

这个题目如果是第一次接触还是比较困难的,我们根据题目可以得到此题的目标是求出给定挤奶网格的最小覆盖矩形的面积大小。

对于二维的情况,我们暂时是不太好处理的. 那我们不妨简化一下问题,先考虑一维的情况,即如何对于给定的一位数组求出其最小覆盖子数组的长度.

那么,此处我们就需要利用KMP模式匹配的变种来解决覆盖问题.

传统的KMP模式匹配算法可以计算出一个最长子串S’,使得S’既是原字符串的前缀又是后缀,那这样的结果如何帮助我们来求最小覆盖子串呢?

我们考虑S-S’形成的剩余子串,这个子串一定可以覆盖整个原字符串. (这个子串可以覆盖前缀S’的开始部分,也可以覆盖后缀S’的开始部分,而后缀S’的开始部分又是前缀S’的第二部分…以此类推可以完全覆盖)

又因为KMP求出的S’是最长的符合要求的前后缀字符串,所以这个覆盖子串也是最小的.

根据这种方式,我们求出最小覆盖字符串的长度就是len-next[len].

接下来我们需要将一维的情况拓展到二维.

一种简单的拓展方式是,对于每一行我们都求出一个最小覆盖字符串长度,然后对所有行求最小公倍数;接下来对每一列也做同样的操作,求出最小公倍数,将两者相乘得到结果.

这种做法的合理性在于:每一行的最小覆盖子串的长度最小公倍数一定可以覆盖所有行,列同理,把这两者拼到一起,就是最小覆盖矩形.

但是这种方式需要我们求C*R次KMP特征向量,有没有更简单的方式?

另一种方式就是:我们将每一行字符串看作一个整体,对原来的二维数组和转置后的二维数组分别进行一次KMP算法,将len-next[len]得到的结果相乘.

例如:样例中的矩阵为(第一行:ABABA,第二行:ABABA)

我们将每一行看作一个整体,那么得到了一个两元素的字符串,每个元素都是ABABA. 我们对这个字符串做一次KMP模式匹配,得到的其实就是方法一的按列计算的最小公倍数. 转置后同样的过程得到方法一按行计算的最小公倍数,最后相乘就可以得到结果.

最关键的步骤其实就在于将一行(列)的字符串看做一个元素,然后对整体矩阵进行KMP,这样就实现了二维情况下的KMP算法.

最终代码:

#include<iostream>
#include<cstring>
using namespace std;
int main(){
    int R,C;
    scanf("%d%d\n",&R,&C);
    char cows[10000][75];
    char t_cows[75][10000];//转置矩阵
    int next_C[76];
    int next_R[10001];
    for(int i=0;i<R;i++){
        for(int j=0;j<C;j++){
            scanf("%c",&cows[i][j]);
        }
        getchar();
    }
    for(int i=0;i<C;i++){
        for(int j=0;j<R;j++){
            t_cows[i][j]=cows[j][i];
        }
    }
    /*将每一行看作一个整体,对二维矩阵进行KMP模式匹配算法*/
    int k1=-1;
    int k2=0;
    next_R[0]=-1;
    while(k2!=R){
        /*这里将原本的str[k1]!=str[k2]改为了字符串比较strncmp*/
        while(k1>=0&&strncmp(cows[k1],cows[k2],C)){
            k1=next_R[k1];
        }
        k1++;
        k2++;
        next_R[k2]=k1;
    }
    /*将每一列看作一个整体,再进行一次KMP模式匹配算法*/
    k1=-1;
    k2=0;
    next_C[0]=-1;
    while(k2!=C){
        while(k1>=0&&strncmp(t_cows[k1],t_cows[k2],R)){
            k1=next_C[k1];
        }
        k1++;
        k2++;
        next_C[k2]=k1;
    }
    printf("%d\n",(R-next_R[R])*(C-next_C[C]));
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值