【BZOJ2085】[Poi2010]Hamsters hash+倍增floyd
【BZOJ2085】[Poi2010]Hamsters
Description
Tz养了一群仓鼠,他们都有英文小写的名字,现在Tz想用一个字母序列来表示他们的名字,只要他们的名字是字母序列中的一个子串就算,出现多次可以重复计算。现在Tz想好了要出现多少个名字,请你求出最短的字母序列的长度是多少。
Input
输入:第一行n(1<=n<=200)和m(1<=m<=10的9此方),n表示有多少个仓鼠,m表示Tz希望出现名字的次数,接下来n行,每行都是仓鼠的名字(中间没有空格)。
Output
输出:一行,最短的字母序列的长度。
Sample Input
4 5
monika
tomek
szymon
bernard
monika
tomek
szymon
bernard
Sample Output
23
题解:先求出任意两个串之间的最长前缀后缀,然后DP。用f[i][j]表示出现了i次,最后一个名字是j的最短长度。用倍增floyd优化即可。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; const ll m1=9997957; const ll m2=1000000007; const int maxn=100010; int n,m,tot; int lp[210],rp[210],bel[maxn]; int head[10000000],next[maxn]; ll val[maxn],mn; char str[maxn]; struct matrix { ll a[210][210]; matrix (){memset(a,0x3f,sizeof(a));} ll* operator [] (int b){return a[b];} matrix operator * (matrix b) { int i,j,k; matrix c; for(k=1;k<=n;k++) for(i=1;i<=n;i++) for(j=1;j<=n;j++) c[i][j]=min(c[i][j],a[i][k]+b[k][j]); return c; } }tr,ans; void add(int a,int b,ll c) { val[b]=c,next[b]=head[a],head[a]=b; } void find(int b,int a,ll c) { for(int i=head[a];i;i=next[i]) if(rp[bel[b]]-b==i-lp[bel[i]]+1&&c==val[i]&&!(bel[b]==bel[i]&&b==lp[bel[b]]&&i==rp[bel[i]]-1)) tr[bel[b]][bel[i]]=min(tr[bel[b]][bel[i]],(ll)rp[bel[i]]-i-1); } void pm(int y) { while(y) { if(y&1) ans=ans*tr; tr=tr*tr,y>>=1; } } int main() { scanf("%d%d",&n,&m); int i,j; ll h1,h2,b1,b2; for(i=1;i<=n;i++) { lp[i]=rp[i-1],scanf("%s",str+lp[i]),rp[i]=strlen(str); for(h1=h2=0,j=lp[i];j<rp[i];j++) bel[j]=i,h1=(h1*233+str[j])%m1,h2=(h2*233+str[j])%m2,add(h1,j,h2); } for(i=1;i<=n;i++) for(j=1;j<=n;j++) tr[i][j]=rp[j]-lp[j]; for(i=1;i<=n;i++) { for(h1=h2=0,b1=b2=1,j=rp[i]-1;j>=lp[i];j--) { h1=(h1+b1*str[j])%m1,h2=(h2+b2*str[j])%m2,b1=b1*233%m1,b2=b2*233%m2; find(j,h1,h2); } } for(i=1;i<=n;i++) ans[i][i]=rp[i]-lp[i]; pm(m-1); for(mn=1ll<<60,i=1;i<=n;i++) for(j=1;j<=n;j++) mn=min(mn,ans[i][j]); printf("%lld",mn); return 0; }
| 欢迎来原网站坐坐! >原文链接<