KMP求最小覆盖子矩阵 - poj2185

Milking Grid


题意:在字符矩阵中找出一个最小子矩阵,使其多次复制所得的矩阵包含原矩阵。

数据范围:1 <= R <= 10,000, 1 <= C <= 75

思路1:
先用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\n",ans_c*ans_r);
    return 0;
}


思路2
找出每行的重复子串长度的各种可能情况,然后每行都有的并且是最小长度作为宽width。
第二步找最小重复子矩阵的高,取每行的宽为width的前缀作为一个单位,对这0到r-1个单位求出KMP的next函数,
找出最小重复子序列的单位数作为高height,最终答案为width*height。


#include<stdio.h>
#include<string.h>
char s[10010][80];
int next[10010];
int main()
{
    int i,j,x,y,r,c,f[80];
    char a[80];
    scanf("%d%d",&r,&c);
    for(i=0; i<c; i++)f[i]=0;
    for(i=0; i<r; i++)
    {
        scanf("%s",s[i]);
        strcpy(a,s[i]);
        //将每行的每种重复子串长度都求出来
        for(j=c-1; j>0; j--)
        {
            a[j]=0;
            for(x=0,y=0; s[i][y]; x++,y++)
            {
                if(!a[x])x=0;
                if(a[x]!=s[i][y])break;
            }
            if(!s[i][y])f[j]++;
        }
    }
    for(i=0; i<c; i++) //找出所有行的最小相同的子串长度,为最小重复子矩阵的列数
        if(f[i]==r)break;
    x=i;//最小重复子矩阵的列数
    for(i=0; i<r; i++)s[i][x]=0;
    next[0]=-1;//按纵列求KMP的next函数,以求最小重复子矩阵的行数
    for(i=1,j=-1; i<r; i++)
    {
        while(j!=-1&&strcmp(s[j+1],s[i]))j=next[j];
        if(!strcmp(s[j+1],s[i]))j++;
        next[i]=j;
    }
    printf("%d\n",(r-1-next[r-1])*x);//行列相乘即为最终结果
    return 0;
}


猜你喜欢

转载自blog.csdn.net/exchan/article/details/78209016
今日推荐