码迷,mamicode.com
首页 > 其他好文 > 详细

【BZOJ2085】[Poi2010]Hamsters hash+倍增floyd

时间:2017-08-15 19:58:24      阅读:139      评论:0      收藏:0      [点我收藏+]

标签:while   最长前缀   ret   std   zoj   desc   next   const   sof   

【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

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;
}

【BZOJ2085】[Poi2010]Hamsters hash+倍增floyd

标签:while   最长前缀   ret   std   zoj   desc   next   const   sof   

原文地址:http://www.cnblogs.com/CQzhangyu/p/7366935.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!