BZOJ2085: [Poi2010]Hamsters

111 篇文章 0 订阅
3 篇文章 0 订阅

题目大意:给出n个互不包含的字符串,要求你求出一个最短的字符串S,使得这n个字符串在S中总共至少出现m次,问S最短是多少


我们构造这样一个矩阵A:

A[i][j]表示第j个字符串长度-第i个字符串的最长是第j个字符串前缀的后缀长度

也就是说,若当前字符串的末尾是第i个字符串,想让下一个出现的字符串是j的话,需要至少在后面添加多少个字母

由于m很大,我们可以想象用类似矩阵快速幂求方案数的方法类似的来求解,只不过把先乘后加转化成了先加后取min

为什么管这个东西叫倍增floyd....反正我觉得就是矩阵乘法...


#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char s[201][100001];
long long n,m;
struct ppp
{
	long long w[201][201];
	void print()
	{
		int i,j;
		for(i=1;i<=n;i++)
		{for(j=1;j<=n;j++) cout<<w[i][j]<<' ';cout<<endl;}
	}
}st;
ppp operator *(const ppp &x,const ppp &y)
{
	ppp ans;
	memset(ans.w,0x3f,sizeof(ans.w));
	int i,j,k;
	for(i=1;i<=n;i++)
	for(j=1;j<=n;j++)
	for(k=1;k<=n;k++)
	ans.w[i][j]=min(ans.w[i][j],x.w[i][k]+y.w[k][j]);
	return ans;
}
ppp ksm(ppp d,long long c)
{
	if(c==0) return st;
	st=d;
	c--;
	while(c)
	{
		if(c&1) st=st*d;
		d=d*d;
		c/=2;
	}
	return st;
}
long long get(long long x,long long y)
{
	long long lx=strlen(s[x]),ly=strlen(s[y]);
	long long k=min(lx,ly),i;
	if(x==y) k--;
	bool f=true;
	while(k)
	{
		f=true;
		for(i=1;i<=k;i++)
		if(s[x][lx-k-1+i]!=s[y][i-1]) {f=false;break;}
		if(f) return k;
		k--;
	}
	return 0;
}
int main()
{
	/*freopen("cho.in","r",stdin);
	freopen("cho.out","w",stdout); */
	scanf("%lld%lld",&n,&m);
	long long i,j;
	for(i=1;i<=n;i++)
	scanf("%s",s[i]);
	ppp di;
	for(i=1;i<=n;i++)
	for(j=1;j<=n;j++)
	di.w[i][j]=strlen(s[j])-get(i,j);
	memset(st.w,0,sizeof(st.w));
	ppp ans=ksm(di,m-1);
	long long anss=0x3f3f3f3f3f3f3f3fll;
	for(i=1;i<=n;i++)
	for(j=1;j<=n;j++)
	anss=min(anss,(long long)(ans.w[i][j]+(long long)strlen(s[i])));
	cout<<anss;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值