POJ2185 Milking Grid [KMP应用]

  一道KMP的好题,可惜数据实在是太弱了。

  给出R*C的字符矩阵,问至少多大面积的小矩阵可以覆盖掉整个大矩阵。很容易想到的解法就是求出循环节然后求最大公倍数,但是这样就忽视了可以不用正好覆盖这个条件,比如aaabaa可以拆成aaab/aa,aaaba/a,aaabaa,而不一定非要拆成4的倍数。正确的做法是求出每一列可能拆分的长度,然后取所有列都可以分的长度中最小的。比如

  abcdeaa
  aaabaaa

  第一个串可以分的长度有5,7,第二个串可以分的长度有4,5,6,7,所以取5作为公共长度。至于怎么求一个字符串所有可以拆分的情况,用KMP处理一遍就可以了,然后从最后一列开始,取i=next[i]直到i=-1,这中间的i-next[i]值都是可取长度,比如aabaabaa,它的next数组是01012345,从最后一列开始,next[8]=5,有8-5=3可取,next[5]=2,有8-2=6可取,next[2]=1,有8-1=7可取,next[1]=0,有8-0=8可取,最终可取的数有3,6,7,8四个数。至于为什么这样做是可行的,想想next数组的性质就知道了,next[i]表示以i结尾的串的前缀和后缀的匹配程度。

  算出宽度后只要将每行的前width个字符作为一个整体,然后KMP求出最小循环节作为height,求出乘积即可。

#include <stdio.h>
#include <string.h>
int r,c;
char mz[10005][80];
int ctot[80],next[10005];
void kmp_next(char *s){
    memset(next,0,5*c);
    next[1]=0,next[0]=-1;
    for(int i=2,j=0;i<=c;i++){
        while(j>0&&s[j+1]!=s[i])j=next[j];
        if(s[j+1]==s[i])j++;
        next[i]=j;
    }
    for(int i=next[c];i!=-1;i=next[i]){
        ctot[c-i]++;
    }
}
int kmp_col(){
    memset(next,0,sizeof next);
    next[1]=0;
    for(int i=2,j=0;i<=r;i++){
        while(j>0&&strcmp(mz[j+1]+1,mz[i]+1))j=next[j];
        if(strcmp(mz[j+1]+1,mz[i]+1)==0)j++;
        next[i]=j;
    }
    return r-next[r];
}
int solve(){
    memset(ctot,0,sizeof ctot);
    for(int i=1;i<=r;i++){
        kmp_next(mz[i]);
    }
    int width=0;
    for(int i=1;i<=c;i++){
        if(ctot[i]==r){
            width=i;
            break;
        }
    }
    for(int i=1;i<=r;i++){
        mz[i][width+1]='\0';
    }
    return width*kmp_col();
}
int main(){
    while(scanf("%d%d",&r,&c)!=EOF){
        for(int i=1;i<=r;i++){
            scanf("%s",mz[i]+1);
        }
        int x=solve();
        printf("%d\n",x);
    }
    return 0;
}

   

原文地址:https://www.cnblogs.com/swm8023/p/2620853.html