題意:先對於一個n*m的字符矩陣S,求一個S中最小的矩陣t,使得這個矩陣可以通過複製,組成一個大矩陣T,T可以完全覆蓋S。注意,這裏S和T不必要相等,只要T中和S重合的那部分完全等於S即可。
思路:網上通行的題解方案其實是不可取的,能AC.....只能說數據比較弱。這道題很多題解說求每行最小重複子串長度的最小公倍數,每列最小重複子串長度的最小公倍數。其實這是很容易看出反例的。因爲並不要求矩陣T和S相等,而是包含關係,右下方是可以多餘的。
例:aaabaa
可以用 aaab 複製兩次得到 aaabaaab 來覆蓋 aaabaa。也可以用aaaba 複製兩次得到aaabaaaaba 來覆蓋aaabaa。前一種是最小方案。
這道題理解了kmp中的next指針(程序中指fail)也就不難了,建議黃超神牛的kmp詳解http://blog.csdn.net/my_gemini_acm/article/details/8248183對於長度爲m的一個串s[i],顯然m-next[m],m-next[next[m]],...... 都是能通過複製,完全覆蓋字符串的可行串,用kmp得到所有可行的方案。對所有s[i]都可行的最小的劃分方案即爲最終的寬度w。至於矩陣的高度h,我們只需要把每個s[i] 的前w個字符當做一個整體,記作W[i] ,對W[1],W[2],...... 作kmp,得到豎着的最小重複“子串”,即爲高h。
例:aaabaaa
aaaabaa
aaabaaa
aaaabaa
對s[1] 可行劃分爲aaab 、aaaba 、aaabaa 、aaabaaa ,即4 5 6 7
對s[2] 可行劃分爲aaaab 、aaaaba 、aaaabaa ,即5 6 7
對s[3] 可行劃分爲aaab 、aaaba 、aaabaa 、aaabaaa ,即4 5 6 7
對s[4] 可行劃分爲aaaab 、aaaaba 、aaaabaa ,即5 6 7
我們取最小的w=5,得到W[1]=aaaba,W[2]=aaaab ,W[3]=aaaba ,W[4]=aaaaba
繼續做kmp,得到W的最小重複“子串”爲W[1]W[2],即h=2。
故而最終結果面積爲w*h
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
using namespace std;
const int maxn=10011;
int fail[maxn];
int num[100];
char s[10001][100];
void kmp_1(char a[100])
{
int i,j;
j=-1;
memset(fail,-1,sizeof(fail));
for(i=1;i<strlen(a);i++)
{
while(j>-1 && a[j+1]!=a[i]) j=fail[j];
if(a[j+1]==a[i]) j++;
fail[i]=j;
}
return ;
}
void kmp_2(int n)
{
int j=0;
memset(fail,0,sizeof(fail));
for(int i=2;i<=n;i++)
{
while(j>0 && (strcmp(s[j+1],s[i])!=0))
j=fail[j];
if(strcmp(s[j+1],s[i])==0) j++;
fail[i]=j;
}
return ;
}
void work(char a[100])
{
int n=strlen(a)-1;
int j=n;
while(j>=0)
{
num[n-fail[j]]++;
j=fail[j];
}
return ;
}
int main()
{
int N,M;
while(cin>>N>>M)
{
memset(num,0,sizeof(num));
for(int i=1;i<=N;i++)
{
scanf("%s",s[i]); //擔心cin會超時....
kmp_1(s[i]);
work(s[i]);
}
bool flag=0;
int w,h;
for(int i=1;i<=M;i++)
if(num[i]==N)
{
w=i;
flag=1;
break;
}
kmp_2(N);
h=N-fail[N];
cout<<w*h<<endl;
}
return 0;
}