P6681
==Ambiguous Encoding。orz Wu_ren。
直接选两个拼成的字符串不好刻画,考虑增量。定义一对合法的字符串 \(S,T\) 满足其中一个是另一个的前缀(这样才可能通过往后面加模式串变一样),每次往当前两个字符串中长度更短的后面塞一个模式串,使得一个字符串仍是另一个的前缀。
容易发现,任何时刻 \(\left||S|-|T|\right|\le m\)。所以此时可以考虑一个 dp:设 \(dp_{i,j}\) 为当前更长的字符串最后的模式串编号为 \(i\),\(\left||S|-|T|\right|=j\) 的状态最少需要多长的字符串拼成。
考虑如何转移:可以枚举一个当前最后模式串编号 \(i\),串长差 \(j\),新接上的模式串编号 \(k\),通过 string 的 substr 函数判断是否可以转移,额外加上的贡献就是新的 \(\max(|S|,|T|)\) 减之前的。
然而这个转移是有环的,所以把 dp 变为 dis,转化为最短路做。答案即为 \(\min dis_{i,0}\)。
这题自然是可以轻松过的,但是 Ambiguous Encoding 看起来不行(\(n=1000,m=16\)),因为这似乎是 \(O(E\log V)\) 即 \(O(n^2m \log nm)\) 的。但事实上真是这样的吗?
发现边权不超过 \(m\),所以每个点被松弛的次数也不超过 \(m\),大概可以考虑每个时刻堆里面的数的极差不超过 \(m\)。有更好的证明可以教教我。
所以时间复杂度是 \(O(nm\log nm)\) 的,两题都轻松通过。
细节有一些,尤其注意当第 \(k\) 个模式串 \(|S_k|\le j\) 时,\(dp_{i,j}\) 还是可能从 \(k\) 这里转移的,这一步要注意分讨一下。
code:
点击查看代码
#define ID(i,j) ((i-1)*(m+1)+j+1)
int n,m,dis[N];
string s[N];bool vis[N];
priority_queue<pii> q;
int tot,head[N];
struct node{int to,nxt,cw;}e[M];
il void add(int u,int v,int w){e[++tot]={v,head[u],w},head[u]=tot;}
void Yorushika(){
scanf("%d%d",&n,&m);
rep(i,1,n)cin>>s[i];
rep(i,1,n){
int l=s[i].size();
rep(j,0,l-1){
rep(k,1,n){
int len=l-j;
if(k==i&&!j)continue;
if(s[k].size()>=len){
if(s[i].substr(j,len)==s[k].substr(0,len)){
int w=s[k].size()-len;
add(ID(i,len),ID(k,w),w);
}
}else{
int sz=s[k].size();
if(s[i].substr(j,sz)==s[k].substr(0,sz)){
int w=len-sz;
add(ID(i,len),ID(i,w),0);
}
}
}
}
}
mems(dis,0x3f);
rep(i,1,n){
int u=ID(i,s[i].size());
dis[u]=s[i].size(),q.push(Mp(-dis[u],u));
}
while(q.size()){
int u=q.top().se;q.pop();
if(vis[u])continue;
vis[u]=1;
go(i,u){
int v=e[i].to;
if(dis[v]<=dis[u]+e[i].cw)continue;
dis[v]=dis[u]+e[i].cw,q.push(Mp(-dis[v],v));
}
}
int ans=inf;
rep(i,1,n)ans=min(ans,dis[ID(i,0)]);
printf("%d\n",ans<1e9?ans:-1);
}
signed main(){
int t=1;
// scanf("%d",&t);
while(t--)
Yorushika();
}