首先,透過預處理計算每個名字的雜湊值,然後利用雜湊檢查名字之間的最長公共字首,從而確定從一個名字轉移到另一個名字的最小代價。
使用倍增動態規劃計算轉移的最小成本,\(f_{t,i,j}\) 表示從名字 \(i\) 經過 \(2^t\) 步轉移到名字 \(j\) 的最小代價,最後透過位運算處理 \(M\) 次轉移的組合,最終得到答案。
程式碼:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=210,L=100010,mod=19260817;
char s[N][L];
ll f[35][N][N],dis[N],tmp[N],n,m,ans;
int len[N],hashh[N][L],pow_26[L];
bool check(int x,int y,int l) {
return (ll)(hashh[x][len[x]-1]-hashh[x][len[x]-l-1]+mod)%mod==(ll)((ll)hashh[y][l-1]*pow_26[len[x]-l]%mod);
}
void init() {
pow_26[0]=1;
for(int i=1; i<=100000; i++)
pow_26[i]=pow_26[i-1]*26%mod;
memset(f,0x3f,sizeof(f));
}
int main() {
init();
scanf("%lld%lld",&n,&m);
m--;
for(int i=1; i<=n; i++) {
scanf("%s",s[i]);
len[i]=strlen(s[i]);
dis[i]=len[i];
for(int j=0; j<len[i]; j++)
if(j) hashh[i][j]=(hashh[i][j-1]+pow_26[j]*(s[i][j]-'a'+1))%mod;
else hashh[i][j]=s[i][j]-'a'+1;
}
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++) {
int l=min(len[i],len[j]);
for(int k(i==j?l-1:l); k; --k)
if(check(i,j,k)) {
f[0][i][j]=len[j]-k;
break;
}
if(f[0][i][j]>10000000) f[0][i][j]=len[j];
}
for(int t=1; t<=30; t++)
for(int k=1; k<=n; k++)
for(int j=1; j<=n; j++)
for(int i=1; i<=n; i++)
f[t][i][j]=min(f[t-1][i][k]+f[t-1][k][j],f[t][i][j]);
for(int i=0;i<=30;i++)
if(m&(1<<i)) {
for(int j=1; j<=n; j++) {
tmp[j]=0x3f3f3f3f3f3f3f3fll;
for(int k=1; k<=n; k++)
tmp[j]=tmp[j]<dis[k]+f[i][k][j]?tmp[j]:dis[k]+f[i][k][j];
}
for(int j=1; j<=n; j++)
dis[j]=tmp[j];
}
ans=0x3f3f3f3f3f3f3f3fll;
for(int i=1; i<=n; i++)
ans=min(ans,dis[i]);
printf("%lld\n",ans);
return 0;
}