• fjwc2019 D1T3 不同的缩写(dinic+trie+dfs)


    #180. 「2019冬令营提高组」不同的缩写

    乍看之下没有什么好的方法鸭.......于是考虑暴力。

    长度?二分似乎可行。

    于是我们二分最长子串的长度(设为$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
    ",ans);
        for(int i=1;i<=n;++i) cout<<name[i]<<endl;
        return 0;
    }
  • 相关阅读:
    Python pip 下载速度慢? Windows 设置 国内源,用阿里云国内镜像加速
    Go timer 是如何被调度的?
    Go sync.Pool 浅析
    一次错误使用 go-cache 导致出现的线上问题
    golang面向对象分析
    一文完全掌握 Go math/rand
    这一次,彻底搞懂 Go Cond
    面试题:让你捉摸不透的 Go reslice
    当 Go struct 遇上 Mutex
    这可能是最容易理解的 Go Mutex 源码剖析
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/10428716.html
Copyright © 2020-2023  润新知