题意:在字符矩阵中找出一个最小子矩阵,使其多次复制所得的矩阵包含原矩阵。首先我们计算答案矩阵的列数,叫做宽。对每行都计算出所有的重复子串可能的长度,如:AAAABAAA,可能的重复子串长度为:5,6,7,8.如何计算呢?k从m开始,每次k-fail[k]即为重复子串长度,k=fail[k],直到k=0.每行中都出现过的最小长度即为答案矩阵的宽。(遇到一个可能长度就计数器+1,最后从小到大扫一遍,次数为n的即可)。然后我们要计算答案矩阵的行数。那么就把每一行长度为答案矩阵的宽的前缀取出来作为一个单位,进行二维kmp(每次比较一个字符串。)其实和一维一模一样hh。。然后这样的最小重复子串长度就是答案矩阵的行数。
#include <cstdio>
#include <cstring>
#define N 10010
int n,m,fail[N],f[80];
char s[N][80];
inline void getfail(char *t){
int k=0;fail[1]=0;
for(int i=2;i<=m;++i){
while(k&&t[k+1]!=t[i]) k=fail[k];
if(t[k+1]==t[i]) ++k;
fail[i]=k;
}
}
int main(){
// freopen("a.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%s",s[i]+1);
for(int i=1;i<=n;++i){
getfail(s[i]);
for(int k=m;k;k=fail[k]) f[m-fail[k]]++;
}
for(int i=1;i<=m;++i) if(f[i]==n) m=i;
for(int i=1;i<=n;++i) s[i][m+1]=0;
int k=0;fail[1]=0;
for(int i=2;i<=n;++i){
while(k&&strcmp(s[k+1]+1,s[i]+1)) k=fail[k];
if(strcmp(s[k+1]+1,s[i]+1)==0) ++k;
fail[i]=k;
}
printf("%d\n",m*(n-fail[n]));
return 0;
}