poj2185 kmp经典好题!

来源:互联网 发布:crf算法 python 编辑:程序博客网 时间:2024/06/04 18:25

题意:先对于一个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;}

原创粉丝点击