题意:传送门
题解:首先需要了解对于一个字符串的循环节的求法,如果是完美的,也就是
i
%
(
i
−
N
e
x
t
[
i
]
)
=
=
0
i\%(i-Next[i])==0
i%(i−Next[i])==0的情况,那么最小的循环节就是
i
−
N
e
x
t
[
i
]
i-Next[i]
i−Next[i],其他的循环节就是它的倍数了,如果是不完美的,那么最小循环节也是
i
−
N
e
x
t
[
i
]
i-Next[i]
i−Next[i],只不过是之后循环节不一定是它的倍数了,比如
a
a
a
b
a
a
aaabaa
aaabaa,算出的
i
−
N
e
x
t
[
i
]
=
=
2
i-Next[i]==2
i−Next[i]==2,循环节就是
4
4
4,但是
5
,
6
5,6
5,6也可以,不一定是倍数了,所以对于每行的循环节长度求出来是没有意义的,何况行数只有
75
75
75的数据范围,直接可以暴力,不行可以hash,之后算出最小的宽度后,可以对于行数直接使用
k
m
p
kmp
kmp,直接可以将对应的循环节看成一个字符,然后行就是一串字符串了,之后求出
n
−
N
e
x
t
[
n
]
n-Next[n]
n−Next[n]就是最小的长度了,但是有时候宽度边长后,对应的长度是否会变短呢?这个是不可能的,如果宽度变长,对应的行数上字符比较就会越来越多,长度只会变得越来越长,只会使得情况越来越糟糕,所以暴力枚举出宽度,长度使用
k
m
p
kmp
kmp线性求出,最后相乘就是答案了。
c
o
d
e
:
code:
code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e4+5,M=80;
char str[N][M];
int n,m,Next[N];
bool st[M];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>str[i];
for(int j=1;j<=m;j++){
bool is_match=true;
for(int k=j;k<m;k+=j){
for(int u=0;u<j&&k+u<m;u++){
if(str[i][u]!=str[i][k+u]){
is_match=false;
break;
}
}
if(!is_match)break;
}
if(!is_match)st[j]=true;
}
}
int width;
for(int i=1;i<=m;i++){
if(!st[i]){
width=i;
break;
}
}
for(int i=1;i<=n;i++)str[i][width]=0;
for(int i=2,j=0;i<=n;i++){
while(j&&strcmp(str[i],str[j+1]))j=Next[j];
if(!strcmp(str[i],str[j+1]))j++;
Next[i]=j;
}
int height=n-Next[n];
cout<<width*height<<endl;
return 0;
}