题意:
给出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; }
原文地址:http://blog.csdn.net/ww140142/article/details/48135919