poj2185 kmp經典好題!

題意:先對於一個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;
}
		
		
	

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章