字符串KMP || POJ 2185 Milking Grid

求一个最小矩阵,经过复制能够覆盖原矩阵(覆盖,不是填充,复制之后可以有多的)

*解法:横着竖着kmp,求最大公倍数的做法是不对的,见http://blog.sina.com.cn/s/blog_69c3f0410100tyjl.html

正解如http://poj.org/showmessage?message_id=153316

求出每一行的可以重复的子串长度,所有行都可以的(公共的)那个最小的长度就是最终所求矩阵的宽

求出宽之后把剩下那些列砍掉,然后要求矩阵的高,把每行看作一个整体kmp即可

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define SZ 10005
int nxt[SZ];
char s[SZ][80], tmp[80];
int r, c, k, cnt[80];//cnt相当于桶,记录每个长度出现多少次,如果一个长度出现r次即表示这个长度在每行都出现过,即为最终矩阵的宽
void get_next(char p[], int plen)
{
    int i = 0, j = -1;
    nxt[0] = -1;
    while(i < plen)
    {
        if(j == -1 || p[i] == p[j])
        {
            i++;
            j++;
            nxt[i] = j;
        }
        else j = nxt[j];
    }
    k = c - nxt[plen];//后缀
    cnt[k]++;
    if(k == c) return;
    for(int i = 0; i < nxt[plen]; i++)
        tmp[i] = p[i + plen - nxt[plen]];//把后缀拿出来,再kmp
    get_next(tmp, nxt[plen]);
    return;
}
void get_next_str(char p[][80], int plen)
{
    int i = 0, j = -1;
    nxt[0] = -1;
    while(i < plen)
    {
        if(j == -1 || strcmp(p[i], p[j]) == 0)//把每行看作一个整体
        {
            i++;
            j++;
            nxt[i] = j;
        }
        else j = nxt[j];
    }
    return;
}
int main()
{
    scanf("%d %d", &r, &c);
    for(int i = 0; i < r; i++)
        scanf(" %s", s[i]);
    for(int i = 0; i < r; i++)
        get_next(s[i], c);
    for(int i = 0; i < c; i++)
        if(cnt[i] == r)
        {
            c = i;//i长度出现了r次,即为最终的宽
            break;
        }
    for(int i = 0; i < r; i++)
        s[i][c] = '\0';//把剩下的列砍掉
    get_next_str(s, r);
    printf("%d\n", c * (r - nxt[r]));
    return 0;
}

相关推荐