fjwc2019 D1T3 不同的缩写(dinic+trie+dfs)
乍看之下没有什么好的方法鸭.......于是考虑暴力。
长度?二分似乎可行。
于是我们二分最长子串的长度(设为$len$),蓝后暴力查找。
先在每个串内练好后继边建图
for(int i=1;i<=n;++i){ int len=strlen(a[i]+1); for(int j=0;j<26;++j) To[i][len][j]=-1; for(int j=len-1;j>=0;--j){ memcpy(To[i][j],To[i][j+1],sizeof(To[i][j+1])); To[i][j][a[i][j+1]-'a']=j+1; } }
每次用dfs查找一个串中长度不超过$len$的子串个数。
一个重要的剪枝:当长度不超过$len$的子串个数已经超过n个时,显然我们可以停止查找。因为显然无论怎么配,这个串都有解。
现在,我们筛掉了一定有解的串,那么对于剩下的串我们怎么判断答案为$len$时是否有解?
考虑暴力建出一棵trie树,保存剩下串的信息。
接下来我们实现匹配。
首先我们先新建总起点,终点$S,T$;以及$n-w$个点表示对应的子串(前面剪枝剪掉的子串(设为$w$个)就不用建点辣)
(当然你可以方便地直接开n个点)
我们遍历子串$k$,当访问到树上的某个节点$p$时,从$k$向$p$连一条流量为1的边(没错!我们等下要跑网络流),表示一种匹配。
所有串遍历完后,trie上的每个点都向$T$连一条流量为1的边。
最后,$S$向$n-w$个代表子串的点连一条流量为1的边。
于是我们就可以从$S$到$T$愉快地跑一遍dinic辣
找到了最短的长度,现在考虑输出其中一种方案。
直接上dfs遍历每一个串,如果找到一个还未打上结束标记的节点,就把这个节点的结束标记打上该串的编号,然后跳出dfs,遍历下个串。
最后开个字符栈,再跑遍dfs。
蓝后就是艰难(for me)的打code了
#include<iostream> #include<cstdio> #include<cstring> #include<queue> #define stop system("pause") using namespace std; inline int min(int a,int b){return a<b?a:b;} const int inf=2147483647; #define N 305 #define M 500005 char a[N][N]; bool vis[M]; int n,ans=-1,To[N][N][27],tot,is[N]; int d[M],cur[M],S,T; int tri,p[M][27],fr[M]; int cnt,hd[M],nxt[M],ed[M],poi[M],cap[M]; queue <int> h; string b,name[N]; void clear(int x){for(int i=0;i<26;++i)p[x][i]=0;} void add(int x,int y,int v){ nxt[ed[x]]=++cnt; hd[x]=hd[x]?hd[x]:cnt; ed[x]=cnt; poi[cnt]=y; cap[cnt]=v; } void ins(int x,int y,int v){add(x,y,v);add(y,x,0);} #define to poi[i] bool bfs(){ memset(vis,0,sizeof(vis)); memset(d,-1,sizeof(d)); h.push(S); vis[S]=1; d[S]=0; while(!h.empty()){ int x=h.front(); h.pop(); for(int i=hd[x];i;i=nxt[i]){ if(!vis[to]&&cap[i]>0){ vis[to]=1; d[to]=d[x]+1; h.push(to); } } }return vis[T]; } int dfs(int x,int a){//dinic if(x==T||a==0) return a; int F=0,f; for(int &i=cur[x];i&&a>0;i=nxt[i]) if(d[to]==d[x]+1&&(f=dfs(to,min(cap[i],a)))>0) cap[i]-=f,a-=f,cap[i^1]+=f,F+=f; return F; } int dinic(){ int re=0; while(bfs()){ for(int i=1;i<=tri+2;++i) cur[i]=hd[i]; re+=dfs(S,inf); } return re; } void dfs1(int d,int id,int o,int dl){ if(d) ++tot; if(d==dl||tot>=n) return; for(int i=0;i<26&&tot<n;++i) if(To[id][o][i]!=-1) dfs1(d+1,id,To[id][o][i],dl); } void dfs2(int d,int id,int o,int dl,int u){ if(d) ins(id,u,1); if(d==dl) return; for(int i=0;i<26;++i) if(To[id][o][i]!=-1){ if(!p[u][i]) p[u][i]=++tri,clear(tri); dfs2(d+1,id,To[id][o][i],dl,p[u][i]); } } bool chk(int lim){ memset(ed,0,sizeof(ed)); memset(hd,0,sizeof(hd)); memset(nxt,0,sizeof(nxt)); cnt=1; tri=n+1; clear(n+1); for(int i=1;i<=n;++i){ tot=0; is[i]=0; dfs1(0,i,0,lim); if(tot<n) is[i]=1,dfs2(0,i,0,lim,n+1);//剪枝 }S=tri+1;T=tri+2; int tflow=0; for(int i=1;i<=n;++i) if(is[i]) ++tflow,ins(S,i,1); for(int i=n+1;i<=tri;++i) ins(i,T,1); return tflow==dinic(); } int dfs3(int d,int id,int o,int dl,int u){ if(d&&!fr[u]){fr[u]=id; return 1;} if(d==dl) return 0; for(int i=0;i<26;++i) if(To[id][o][i]!=-1){ if(!p[u][i]) p[u][i]=++tri,clear(tri); if(dfs3(d+1,id,To[id][o][i],dl,p[u][i])) return 1; } return 0; } void dfs4(int u){ if(fr[u]) name[fr[u]]=b; for(int i=0;i<26;++i) if(p[u][i]){ b+=(i+'a'); dfs4(p[u][i]); b.erase(b.size()-1); } } int main(){ freopen("diff.in","r",stdin); freopen("diff.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%s",a[i]+1); for(int i=1;i<=n;++i){ int len=strlen(a[i]+1); for(int j=0;j<26;++j) To[i][len][j]=-1; for(int j=len-1;j>=0;--j){ memcpy(To[i][j],To[i][j+1],sizeof(To[i][j+1])); To[i][j][a[i][j+1]-'a']=j+1; } } int l=1,r=n; while(l<r){ int mid=(l+r)/2; if(chk(mid)) r=mid; else l=mid+1; }if(!chk(l)){printf("-1");return 0;} ans=l; for(int i=1;i<=n;++i) if(is[i]) for(int j=hd[i];j;j=nxt[j]) if(cap[j]==0) fr[poi[j]]=i; for(int i=1;i<=n;++i) if(!is[i]) dfs3(0,i,0,ans,n+1); b=""; dfs4(n+1); printf("%d\n",ans); for(int i=1;i<=n;++i) cout<<name[i]<<endl; return 0; }