传送门
题意:给定n个长度总和不超过1e5的字符串,求一个最短的母串,使所有字符串的出现次数之和=m 这n个字符串保证不互相包含,n≤200
思路:
由于保证字符串两两不相互包含。
因此我们可以考虑
d
p
dp
dp。
f
i
,
j
f_{i,j}
fi,j表示所有字符串出现次数之和为
i
i
i,结尾的字符串为第
j
j
j个整个字符串的最短长度。
然后可以枚举字符串转移。
发现可以写成矩阵转移的形式然后用矩阵快速幂优化即可。
代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
inline int Read(char*s){
int top=0;
char ch=gc();
while(!isalpha(ch))ch=gc();
while(isalpha(ch))s[++top]=ch,ch=gc();
return top;
}
inline char get_a_char(){
char ch=gc();
while(!isalpha(ch))ch=gc();
return ch;
}
const int N=205;
int n,m;
const int mod1=1e9+9;
typedef long long ll;
typedef unsigned long long Ull;
const ll inf=1e18;
const Ull bas=311;
inline int add1(const int&a,const int&b){return a+b>=mod1?a+b-mod1:a+b;}
inline int dec1(const int&a,const int&b){return a>=b?a-b:a-b+mod1;}
inline int mul1(const int&a,const int&b){return (ll)a*b%mod1;}
struct Hash_string{
vector<Ull>pw1,s1;
vector<int>pw2,s2;
int len;
inline void init(char*s,int n){
len=n;
pw1.resize(n+1),pw2.resize(n+1),s1.resize(n+1),s2.resize(n+1);
pw1[0]=pw2[0]=1;
for(ri i=1;i<=n;++i){
pw1[i]=pw1[i-1]*bas;
s1[i]=s1[i-1]*bas+(Ull)s[i];
pw2[i]=mul1(pw2[i-1],bas);
s2[i]=add1(mul1(s2[i-1],bas),(int)s[i]);
}
}
inline Ull get1(int l,int r){return s1[r]-s1[l-1]*pw1[r-l+1];}
inline Ull get2(int l,int r){return dec1(s2[r],mul1(s2[l-1],pw2[r-l+1]));}
}S[N];
char s[100005];
typedef long long ll;
struct Mat{
ll a[N][N];
Mat(){for(ri i=1;i<=n;++i)for(ri j=1;j<=n;++j)a[i][j]=inf;}
friend inline Mat operator*(const Mat&a,const Mat&b){
Mat ret;
for(ri i=1;i<=n;++i)for(ri k=1;k<=n;++k)for(ri j=1;j<=n;++j)ret.a[i][j]=min(ret.a[i][j],a.a[i][k]+b.a[k][j]);
return ret;
}
friend inline Mat operator^(Mat a,int p){
Mat ret=a;
for(--p;p;p>>=1,a=a*a)if(p&1)ret=ret*a;
return ret;
}
}trans;
inline int init(int a,int b){
int n=S[a].len,m=S[b].len;
int up=min(n,m),ret=0;
if(a==b)--up;
for(ri i=1;i<=up;++i)if(S[a].get1(n-i+1,n)==S[b].get1(1,i)&&S[a].get2(n-i+1,n)==S[b].get2(1,i))ret=i;
return m-ret;
}
int main(){
n=read(),m=read();
ll mn=inf;
for(ri len,i=1;i<=n;++i){
len=Read(s);
mn=min(mn,(ll)len);
S[i].init(s,len);
}
if(m==1)return cout<<mn,0;
for(ri i=1;i<=n;++i)for(ri j=1;j<=n;++j)trans.a[i][j]=init(i,j);
trans=trans^(m-1);
ll ans=inf;
for(ri i=1;i<=n;++i)for(ri j=1;j<=n;++j)ans=min(ans,S[i].len+trans.a[i][j]);
cout<<ans;
return 0;
}