题意:
给出n个长度不大于100000的字符串;
现在要找出一个字符串包括m个这些字符串;
求这个字符串的最小长度;
数据保证字符串不互相包含;
n<=200,m<=1e9;
题解:
数据保证了字符串没有包含的情况。。
那么为了节约考虑,还是要让字符串叠在一起比较合算;
设f[i][j]表示i后面加个j字符串要再加多少字符;
这个怎么求呢?
Hash之后暴力;
RKhash可以O(1)拿出前缀后缀的Hash值,然后枚举长度就暴力出解了;
这里的复杂度看起来很大,但是PoPoQQQ大爷给了我们一些保障= =;
处理出这个之后,这实际上是一张图了;
要求的就是走m-1步的最短路;
然后就要上一个神奇的Floyd算法:倍增Floyd!
状态变成了三维f[l][i][j]表示i到j走1<<l步的方案数;
转移显然咯:f[l][i][j]=min(f[l-1][i][k]+f[l-1][k][j]);
这样的话,搞到m-1步的话就是将m-1按位分解的再处理了;
然后加上开头的字符串长度,取最小值;
注意字符串到自己本身是可以的,但是枚举长度时要从len-1开始;
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 220
#define M 110000
#define seed 131
using namespace std;
typedef long long ll;
char str[N][M];
unsigned int hash[N][M],pow[M];
ll f[N][N][N],ans[2][N][N],len[N];
ll cmp(int x,int y)
{
for(ll i=x==y?len[x]-1:min(len[x],len[y]);i>=0;i--)
{
if(hash[x][len[x]]-hash[x][len[x]-i]*pow[i]==hash[y][i])
return len[y]-i;
}
}
int main()
{
int n,m,i,j,k,l;
ll x;
scanf("%d%d",&n,&m);
m--;
pow[0]=1;
for(i=1;i<M;i++)
pow[i]=pow[i-1]*seed;
for(i=1;i<=n;i++)
{
scanf("%s",str[i]+1);
len[i]=strlen(str[i]+1);
for(j=1;j<=len[i];j++)
{
hash[i][j]=hash[i][j-1]*seed+str[i][j];
}
}
memset(f,0x3f,sizeof(f));
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
f[0][i][j]=cmp(i,j);
}
}
for(l=1;(1<<l)<=m;l++)
{
for(k=1;k<=n;k++)
{
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
f[l][i][j]=min(f[l][i][j],f[l-1][i][k]+f[l-1][k][j]);
}
}
}
}
bool flag=0;
for(l=0;(1<<l)<=m;l++)
{
if(1<<l&m)
{
if(!flag)
{
flag=1;
memcpy(ans[0],f[l],sizeof(ans[0]));
continue;
}
memset(ans[1],0x3f,sizeof(ans[1]));
for(k=1;k<=n;k++)
{
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
ans[1][i][j]=min(ans[1][i][j],min(ans[0][k][j]+f[l][i][k],ans[0][i][k]+f[l][k][j]));
}
}
}
memcpy(ans[0],ans[1],sizeof(ans[0]));
}
}
for(i=1,x=0x3f3f3f3f3f3f3f3fll;i<=n;i++)
{
for(j=1;j<=n;j++)
{
x=min(x,len[i]+ans[0][i][j]);
}
}
printf("%lld\n",x);
return 0;
}