zoukankan      html  css  js  c++  java
  • POJ 2185 Milking Grid (KMP,求最小覆盖子矩阵,好题)

    题意:给出一个大矩阵,求最小覆盖矩阵,大矩阵可由这个小矩阵拼成。(就如同拼磁砖,允许最后有残缺)

    正确解法的参考链接:
    http://poj.org/showmessage?message_id=153316
    http://blog.sina.com.cn/s/blog_69c3f0410100tyjl.html

    在discuss里还看到有人说可以这么简化:
    求横向最小长度时每次比较整列
    求纵向最小长度时每次比较整行
    真的是太神了!
    http://poj.org/showmessage?message_id=168710

    一开始,我也是按照错误的解法来求得。
    也就是用KMP的next求出每行的最小循环子串长度,然后求这些长度的公倍数,作为宽(若大于col,则为col)。
    然后用KMP的next求出每列的最小循环子串长度,然后求出这些长度的公倍数,作为长(若大于row,则为row)。
    这种解法是过不了下面的样例的:
    Input
    2 8
    ABCDEFAB
    ABCDEABC
    2 8
    ABCDEFAB
    AAAABAAA

    Output
    16
    12
    对于第一个样例,可以这么做。但对于第二个样例,就不行了。
    因为AAAABAAA它的循环子串可以理解为AAAAB,也可以理解为AAAABA,AAAABAA,AAAABAAA
    而这里取AAAABA,正好与第一行的ABCDEF同样为6,所以答案为12。
    但是这样的解法也可以AC,说明POJ数据比较弱。

    思路:先用KMP的next函数求出每行的最小循环子串的长度L,那么L,2L,3L,...都可以作为该行的循环子串。
         此外,还可以存在像AAAABAAA的情况。
         设m为mL<=col的最大值,那么由于列最多75,直接通过暴力比较,判断1...mL+1~col是否可以作为循环子串。
         至于如何获取所求最小矩阵的宽度,这里采用http://poj.org/showmessage?message_id=153316的方法,
            用一个数组统计长度出现的次数,最后从小到大遍历一遍,若出现次数为row的长度,即为我们所求的宽度。
            至于高度的话,用KMP的next,每次比较整行,即可求得最小矩阵的高度。

    #include <iostream>
    #include <stdio.h>
    #include <string.h>
    #include <algorithm>
    
    using namespace std;
    const int maxr=10002;
    const int maxc=80;
    char grid[maxr][maxc]; //大矩阵
    int row,col;//行和列
    int rnext[maxr][maxc]; //rnext[i]:对应第i行字符串的next函数
    int cnext[maxr]; //求纵向的next,每次比较的是整行
    int rlen[maxr]; //rlen[i]:第i行字符串的最小循环子串的长度
    int cnt[maxc];//cnt[i]:统计各宽度出现的次数
    int ans_c,ans_r; //最小覆盖矩阵的宽度和高度
    void rgetNext(int r,char*str){
        int k=0;
        rnext[r][1]=0;
        for(int i=1;i<col;i++){
            while(k&&str[k]!=str[i])
                k=rnext[r][k];
            if(str[k]==str[i])
                k++;
            rnext[r][i+1]=k;
        }
        rlen[r]=col-rnext[r][col];
        int i;
        for(i=rlen[r];i<=col;i+=rlen[r]){
            cnt[i]++;
        }
        i-=rlen[r];
        //直接通过比较来判断,是否还有可能存在的串,如aaabcaaa,除了5,还可能为6,7,8
        //即判断第i+1个字符后的后缀是否和前缀相同
        for(int j=i+1;j<=col;j++){
            int x=0,y=j;//分别从索引0和y处开始比较
            while(str[x]==str[y]){
                x++;y++;
            }
            if(y==col)
                cnt[j]++;
        }
    }
    
    void cgetNext(){
        int k=0;
        cnext[1]=0;
        for(int i=1;i<row;i++){
            while(k&& strcmp(grid[k],grid[i])!=0)
                k=cnext[k];
            if(strcmp(grid[k],grid[i])==0)
                k++;
            cnext[i+1]=k;
        }
        ans_r=row-cnext[row];
    }
    
    int main()
    {
        scanf("%d%d",&row,&col);
        for(int i=0;i<row;i++)
            scanf("%s",grid[i]);
        memset(cnt,0,sizeof(cnt));
        for(int i=0;i<row;i++)
            rgetNext(i,grid[i]);
        cgetNext();
        for(int i=1;i<=col;i++){
            if(cnt[i]==row){
                ans_c=i;
                break;
            }
        }
        printf("%d
    ",ans_c*ans_r);
        return 0;
    }
    View Code
  • 相关阅读:
    886C. Petya and Catacombs#墓室探险(set集合)
    uc/os调度机制深度解析
    总结使用QObject实现线程
    日常总结--c++常量的深入理解
    数据结构与算法--跳跃链表
    java下输出中文的一点研究
    java使用readUTF()读取中文抛出EOFException异常的处理方法
    java连接5.1以上的mysql出现问题---The server time zone value '???ú±ê×??±??' is unrecognized or represents more than one time zone.
    Qt入门便遇到坑--向QMainWindow类及其子类中添加布局
    linux_4.19内核编译总结
  • 原文地址:https://www.cnblogs.com/chenxiwenruo/p/3549967.html
Copyright © 2011-2022 走看看