[Neerc2013]Dictionary

43 篇文章 0 订阅
6 篇文章 0 订阅

题目大意

给你n个长度不超过10的小写字母字符串。
请你构造一颗节点数最少的字典树。
使得对于任意一个人给定字符串,字典树中都存在一条祖先后代链对应的字符串与其相等。

做法

首先显然,如果字符串a包含字符串b,可以直接剔除字符串b。
我们考虑最优解,一定是按照某个顺序添加字符串进入字典树中。
假设现在有字符串c,可以找到其一个最长前缀,使得该前缀可以被字典树表示,然后把剩余后缀加入。
如果c的最长前缀由原本多个字符串的子串构成,第一个是a,第二个是b。
不妨将c的加入顺序调到b之前,解不会变劣。
如此可以发现,最优顺序每一个字符串一定依赖于原本的某个字符串,即它的某个前缀是它的子串。
建立0号点,向第i个点连一条长度为|s[i]|的有向边。
第i个点向第j个点连长度为k的有向边,k是一个最大的整数使得s[j]的前缀k是s[i]的子串。
那么做一个以0为根的最小树形图即可求出答案。
本题要求输出方案,需要逐层展开树形图。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=50+10,inf=100000000;
char s[maxn][maxn];
int belong[maxn][maxn],len[maxn],cnt[maxn],pre[maxn][maxn],dis[maxn][maxn],zz[maxn][maxn];
int data[maxn][maxn][maxn],from[maxn][maxn][maxn],go[maxn][maxn][maxn],In[maxn][maxn],a[maxn][maxn];
bool bz[maxn],pd[maxn],dp[maxn][maxn];
int vis[maxn];
int h[maxn],qx[maxn],nxt[maxn];
int g[500+10][30],fa[500+10],val[500+10],hh[maxn];
int i,j,k,l,t,n,m,tot,top,ans,root;
int getk(int x,int y){
    int i,k,t=0;
    fo(i,1,len[y]){
        k=0;
        while (i+k<=len[y]&&1+k<=len[x]&&s[x][1+k]==s[y][i+k]) k++;
        if (k>t){
            t=k;
            zz[y][x]=i+k-1;
        }
    }
    return t;
}
void update(int id,int x,int y,int s,int t,int v){
    if (v<data[id][x][y]){
        data[id][x][y]=v;
        from[id][x][y]=s;
        go[id][x][y]=t;
    }
}
void work(int id){
    int i,j,k,l=0,r,t;
    int n=cnt[id];
    bool czy=1;
    fo(i,1,n){
        In[id][i]=data[id][0][i];
        a[id][i]=0;
        fo(j,1,n){
            if (data[id][j][i]<In[id][i]){
                In[id][i]=data[id][j][i];
                a[id][i]=j;
            }
        }
        ans+=In[id][i];
    }
    fo(i,1,n) bz[i]=dp[id][i]=vis[i]=0;
    fo(i,1,n)
        if (!bz[i]){
            k=i;
            while (k&&!bz[k]){
                bz[k]=1;
                vis[k]=i;
                k=a[id][k];
            }
            if (k&&vis[k]==i){
                czy=0;
                j=k;
                ++l;
                while (1){
                    dp[id][k]=1;
                    belong[id][k]=l;
                    if (a[id][k]==j) break;
                    k=a[id][k];
                }
            }
        }
    if (czy){
        fo(i,1,n) pre[id][i]=a[id][i];
        return;
    }
    fo(i,1,n)
        if (!belong[id][i]) belong[id][i]=++l;
    fo(i,0,n)
        fo(j,0,n)
            data[id+1][i][j]=inf;
    cnt[id+1]=l;
    fo(i,0,n)
        fo(j,0,n)
            if (belong[id][i]!=belong[id][j]){
                r=belong[id][i];t=belong[id][j];
                /*if (!dp[id][i]&&dp[id][j]) update(id+1,r,t,i,j,data[id][i][j]-In[id][j]);
                else update(id+1,r,t,i,j,data[id][i][j]);*/
                update(id+1,r,t,i,j,data[id][i][j]-In[id][j]);
            }
    work(id+1);
    fo(i,1,n)
        if (!dp[id][i]){
            /*pre[id][i]=a[id][i];*/
            j=belong[id][i];
            pre[id][i]=from[id+1][pre[id+1][j]][j];
        }
        else{
            j=belong[id][i];
            k=go[id+1][pre[id+1][j]][j];
            if (k==i) pre[id][i]=from[id+1][pre[id+1][j]][j];
            else pre[id][i]=a[id][i];
        }
}
void add(int x,int y){
    qx[++tot]=y;
    nxt[tot]=h[x];
    h[x]=tot;
}
void dfs(int x){
    if (x){
        if (pre[0][x]){
            int j=hh[pre[0][x]];
            int t=len[pre[0][x]]-zz[pre[0][x]][x];
            while (t--) j=fa[j];
            int i=len[x]-dis[pre[0][x]][x]+1;
            while (i<=len[x]){
                top++;
                fa[top]=j;
                val[top]=s[x][i]-'a';
                j=top;
                i++;
            }
            hh[x]=j;
        }
        else{
            int i=1,j=root;
            while (i<=len[x]){
                top++;
                fa[top]=j;
                val[top]=s[x][i]-'a';
                j=top;
                i++;
            }
            hh[x]=j;
        }
    }
    int t=h[x];
    while (t){
        dfs(qx[t]);
        t=nxt[t];
    }
}
int main(){
    freopen("dictionary.in","r",stdin);
    //freopen("32","r",stdin);
    freopen("dictionary.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n){
        scanf("%s",s[i]+1);
        len[i]=strlen(s[i]+1);
    }
    fo(i,1,n) pd[i]=1;
    fo(i,1,n)
        fo(j,1,n)
            if (i!=j){
                dis[i][j]=len[j]-getk(j,i);
                if (!dis[i][j]) pd[j]=0;
            }
    top=0;
    fo(i,1,n){
        if (pd[i]){     
            top++;
            fo(k,1,len[i]) s[top][k]=s[i][k];
            len[top]=len[i];
        }
    }
    fo(i,0,n)
        fo(j,0,n)
            dis[i][j]=inf;
    n=top;
    fo(i,1,n)
        fo(j,1,n)
            if (i!=j){
                dis[i][j]=len[j]-getk(j,i);
                if (dis[i][j]==len[j]) dis[i][j]=inf;
            }
    fo(i,1,n) dis[0][i]=len[i];
    cnt[0]=n;
    fo(i,0,n)
        fo(j,0,n)
            data[0][i][j]=dis[i][j];
    work(0);
    fo(i,1,n) add(pre[0][i],i);
    top=root=hh[0]=1;
    dfs(0);
    printf("%d\n",top);
    //printf("%d\n",ans);
    fo(i,1,top)
        if (!fa[i]) printf("0\n");
        else printf("%d %c\n",fa[i],val[i]+'a');
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值