POJ 2185 KMP 二维字符串找最短循环矩阵

主要思想就是降维处理。

相似题目:

HDU 3746:一维字符串找最短循环节,这道题是HDU3746的升级版,增加了一个维度。

题意:

一个字符矩阵看成一个地面,然后把最短循环矩阵看成瓷砖,需要铺满地面,地面的边缘部分,瓷砖可以残缺。

题解:

我们不防把最短循环矩阵拆分成,行的最短循环节,列的最短循环节。

还是直接举例子吧,比较清楚些。

A B A B A
A B A B A

先说行的最短循环节,我们拿一个长度无限的,宽度有限的矩形在上面扫,宽度就是行的最短循环节,上面这个例子,宽度就是2,repetend=”AB”或者”BA”。扫的过程就是KMP,但是我们怎么处理长度无限呢?这里用到降维思想,我们要明白,每一列具体是什么不重要,重要的是每一列是不是相同,所以我们可以把n行加到一行,但是直接相加是不行的,比如说

A B
B A

直接相加的话就变成C C了,所以我们要加个标记 第一列: A 1 + B 2 第二列 B 1 + A 2 ,我们把每个字母乘以它的行号,这样不同行的每个字母都变成独一无二的了。
其实这是种hash的思想,这道题的冲突度很小,就26个字母,所以我们乘个行号就好了。

对应 列的最短循环节也是一样。
还是看代码比较清楚些。

AC代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#define INF 0x3f3f3f3f
using namespace std;

const int maxn = 10010;
const int maxr = 10010;
const int maxc = 80;

char mmap[maxr][maxc];//mmap储存二维字符串
int temp[maxr];
int KMP_next[maxn];

void kmp_pre(int x[], int m, int KMP_next[]) {//kmp未优化版
    //如果多次使用KMP,不需要对next  memset
    //abab  -> next={-1,0,0,1,2};
    int i = 0, j = KMP_next[0] = -1;
    while (i < m) {
        while (j != -1 && x[i] != x[j])
            j = KMP_next[j];
        KMP_next[++i] = ++j;
    }
}
int main(void) {
    ios::sync_with_stdio(false);
    int r, c;
    cin >> r >> c;
    for (int i = 1; i <= r; i++)
        cin >> mmap[i] + 1;
    memset(temp, 0, sizeof temp);
    for (int i = 1; i <= r; i++)//降维处理,把r行合为一行
        for (int j = 1; j <= c; j++) {
            temp[j-1] += mmap[i][j]*i;
        }
    kmp_pre(temp,c,KMP_next);//KMP找到最短循环节
    int repetend_r=c-KMP_next[c];

    memset(temp,0,sizeof temp);
    for (int j = 1; j <= c; j++)//降维处理,把c列合为一列
        for (int i = 1; i <= r; i++) {
            temp[i-1] += mmap[i][j]*j;
        }
    kmp_pre(temp,r,KMP_next);//KMP找到最短循环节
    int  repetend_c=r-KMP_next[r];

    cout<<repetend_c*repetend_r<<endl;//两个循环节长度相乘就是最短循环矩阵的面积
    return 0;
}

猜你喜欢

转载自blog.csdn.net/shadandeajian/article/details/81586976
今日推荐