[Neerc2013]Dictionary

題目大意

給你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');
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章