主要思想就是降维处理。
相似题目:
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
A
∗
1
+
B
∗
2
第二列
B∗1+A∗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;
}