[BZOJ2085] [Poi2010]Hamsters
[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
试题分析
显然两个字符串首尾相交需要重合才可以最小,那么我们可以预处理一下两个字符串有顺序连接的代价。
然后我们就可以倍增转移。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
#define LL long long
inline LL read(){
LL x=0,f=1; char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const LL INF = 1e18;
const LL MAXN = 200010;
const LL Mod = 98152501LL;
const LL base = 131LL;
vector<LL> vec[211][2]; string str[211]; LL N,M;
LL Pw[MAXN+1];LL len[211];
inline void Hash(LL x){
LL las=0; for(LL i=0;i<len[x];i++)
vec[x][0].push_back(las=(las*base%Mod+(str[x][i]-'a'))%Mod);
las=0; for(LL i=len[x]-1;i>=0;i--)
vec[x][1].push_back(las=(las%Mod+(str[x][i]-'a')*Pw[len[x]-i-1])%Mod);
return ;
} LL to[211][211],f[211][211],g[211][211];
inline void Solve(LL x){
if(x==1){
for(LL i=1;i<=N;i++){
for(LL j=1;j<=N;j++)
f[i][j]=to[i][j];
} return ;
} Solve(x>>1);
for(LL i=1;i<=N;i++){
for(LL j=1;j<=N;j++) g[i][j]=f[i][j];
} memset(f,0x3f,sizeof(f));
for(LL i=1;i<=N;i++){
for(LL j=1;j<=N;j++)
for(LL k=1;k<=N;k++)
f[j][k]=min(g[j][i]+g[i][k],f[j][k]);
} if(x&1){
for(LL i=1;i<=N;i++){
for(LL j=1;j<=N;j++) g[i][j]=f[i][j];
} memset(f,0x3f,sizeof(f));
for(LL i=1;i<=N;i++){
for(LL j=1;j<=N;j++){
for(LL k=1;k<=N;k++)
f[j][k]=min(f[j][k],g[j][i]+to[i][k]);
}
}
} return ;
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
N=read(),M=read(); Pw[0]=1; for(LL i=1;i<=MAXN;i++) Pw[i]=Pw[i-1]*base%Mod; LL ret=INF;
for(LL i=1;i<=N;i++) cin>>str[i],len[i]=str[i].length(),Hash(i),ret=min(ret,len[i]);
if(M==1){printf("%lld\n",ret); return 0;}
for(LL i=1;i<=N;i++){
for(LL j=1;j<=N;j++){
LL fg=0; //to[i][j]=len[i]; if(i==j) continue;
for(LL k=(i==j?len[i]-2:min(len[i]-1,len[j]-1));k>=0;k--){
if(vec[i][1][k]==vec[j][0][k]){fg=k+1; break;}
} to[i][j]=len[j]-fg;
}
} Solve(M-1);
LL ans=INF;
for(LL i=1;i<=N;i++){
for(LL j=1;j<=N;j++)
ans=min(ans,len[i]+f[i][j]);
} printf("%lld\n",ans);
return 0;
}
你——悟到了么?