题解 P3502【[POI2010]CHO-Hamsters】
$\text{Upd 2023.9.11}$:修改代码中的小错误。
题意
给出 $n$ 个互不包含的字符串 $a_{1,2,...,n}$,要求你求出一个最短的字符串 $S$,使得这 $n$ 个字符串在 $S$ 中总共至少出现 $m$ 次,问 $\min|S|$。
$\sum|a_i|\le10^5,1\le n\le 200,1\le m \le 10^9$.
思路
很是神仙!!
首先我们想到 $\text{dp}$。
设计状态 $dp_{i,j}$ 表示当前 $S$ 中字符串总出现次数为 $i$,其中最后出现的字符串为第 $j$ 个字符串。
我们先预处理 $dis_{i,j}$ 表示在第 $i$ 个字符串后最少添加多少个字符使得当前字符串中也含有 $j$。
则有边界条件 $dp_{1,j}=|a_j|$,转移方程 $dp_{i,j}=\min(dp_{i-1,k}+dis_{k,j})(1\le k\le n)$。
然后再想想怎么预处理 $dis_{i,j}$。
显然,我们将 $a_i,a_j$ 拉出来做 $\text{KMP}$,然后 $dis_{i,j}$ 就是 $|a_j|\,-$ 最后匹配到的位置。
答案为 $\min_{i=1}^ndp_{m,i}$。
这样就得到了一个 $O(n\sum|a_i|+n^2m)$ 的算法。
我们再看看这个转移方程,很容易想到和最短路相关的算法,然后我们啪的一下就知道了要矩阵优化了。
即经典广义矩阵乘法,若 $A\times B=C$,则 $C_{i,j}=\min(A_{i,k}+B_{k,j})$。
先设定 $dis_{0,i}=|a_i|,dis_{i,0}=\text{INF}$。
我们设定初始矩阵 $A$ 为:$$\begin{bmatrix} dis_{0,0}&dis_{0,1}&dis_{0,2}&...&dis_{0,n}\\ dis_{1,0}&dis_{1,1}&dis_{1,2}&...&dis_{1,n}\\ dis_{2,0}&dis_{2,1}&dis_{2,2}&...&dis_{2,n}\\ &.\\ & &.\\ & & &.\\ dis_{n,0}&dis_{n,1}&dis_{n,2}&...&dis_{n,n} \end{bmatrix} $$
则目标矩阵 $C=A^{m}$,所求即为 $\min_{i=1}^n C_{0,i}$。这显然可以矩阵快速幂。
有一点小细节需要注意,即对于这种广义矩阵乘法我没有找到单位元,所以进行快速幂时我们需要计算 $C=A\times A^{m-1}$。
最终我们得到了一个 $O(n\sum|a_i|+n^3\log m)$ 的优秀算法!
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff
}
const int N=200+10;
const ll INF=1e15;
char a[N][100010];
int nxt[N][100010],n,m,k,len[N];
ll dis[N][N];
struct Matrix{
ll a[N][N];
Matrix(){memset(a,0,sizeof(a));}
inline ll* operator[](const int &x){
return a[x];
}
}bas,ans;
inline Matrix operator*(const Matrix &a,const Matrix &b){
Matrix c;
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
ll s=INF;
for(int k=0;k<=n;k++){
s=min(s,a.a[i][k]+b.a[k][j]);
}
c[i][j]=s;
}
}
return c;
}
int sum;
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
readstr(a[i]);
sum+=len[i]=strlen(a[i]+1);
}
if(sum==n) return printf("%d",m),0;
dis[0][0]=INF;
for(int l=1;l<=n;l++){
k=0;
dis[0][l]=len[l];
dis[l][0]=INF;
for(int i=2;i<=len[l];i++){
while(k&&a[l][i]!=a[l][k+1])k=nxt[l][k];
if(a[l][i]==a[l][k+1]) k++;
nxt[l][i]=k;
}
}
for(int l1=1;l1<=n;l1++){
for(int l2=1;l2<=n;l2++){
k=0;
dis[l1][l2]=len[l2];
for(int i=2;i<=len[l1];i++){
while(k&&a[l1][i]!=a[l2][k+1])k=nxt[l2][k];
if(a[l1][i]==a[l2][k+1]) k++;
if(i==len[l1]){
dis[l1][l2]=len[l2]-k;
}
}
}
}
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
bas[i][j]=dis[i][j];
}
}
ans=bas;
m--;
while(m){
if(m&1) ans=ans*bas;
bas=bas*bas;
m>>=1;
}
ll sum=INF;
for(int i=1;i<=n;i++){
sum=min(sum,ans[0][i]);
}
write(sum);
flush();
}
再见 qwq~

浙公网安备 33010602011771号