Codeforces 965E Short Code [字典树+想法]

时间:2022-01-05 17:09:32

题意

给你n个完全不同的字符串,求将某些字符串改为其前缀,使得新的字符串集,依旧各不相同,且字符串总长度最小。

题解

首先我们字典树建树,字符串的末节点标记为1,这时候得到了一个只有0和1的树,为了将字符串总长度最小,我们肯定要尽量让1的节点在前。因此对于一个权值为0的节点,我们贪心的取其子树下深度最大的节点放置当前节点的位置,这样就可以满足字符串深度最低,所以我们利用multiset维护每个节点下1的深度。在计算答案的时候,对于每个节点,我们首先加上这个节点的子树的1的个数,若当前节点权值为0,则减去深度最大的节点到当前深度的差值,将所有的值求和即可。
(由于字符串总长度不超过10^5,我们直接暴力合并set即可,或者可以用set启发式合并)

AC代码

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<set>
#define N 100005
using namespace std;
typedef long long ll;
char a[N];
multiset<ll,greater<ll> >st[N];
multiset<ll,greater<ll> >::iterator it;
ll tree[N][26],End[N],size[N],tot,root,ans,all;
ll init(ll root)
{
    for(ll i=0;i<26;i++)
        tree[root][i]=-1;
    End[root]=0;
    return root;
}
void insert(char buf[])
{
    ll l=strlen(buf),now=root;
    for(ll i=0;i<l;i++)
    {
        if(tree[now][buf[i]-'a']==-1)tree[now][buf[i]-'a']=init(++tot);
        now=tree[now][buf[i]-'a'];
    }
    End[now]++;
}
void dfs(ll now,ll deep)
{
    size[now]=End[now];
    for(ll i=0;i<26;i++)
    {
        if(tree[now][i]==-1)continue;
        dfs(tree[now][i],deep+1);
        size[now]+=size[tree[now][i]];
        for(it=st[tree[now][i]].begin();it!=st[tree[now][i]].end();it++)
            st[now].insert(*it);
    }
    if(now==0)return ;
    ans+=size[now];
    if(End[now])st[now].insert(deep);
    else if(st[now].size())
    {
        ans-=*st[now].begin()-deep;
        st[now].erase(st[now].begin());
        st[now].insert(deep);
    }
}
int main()
{
    root=init(tot);
    ll n;
    scanf("%lld",&n);
    for(ll i=0;i<n;i++)
    {
        scanf("%s",a);
        insert(a);
    }
    dfs(root,0);
    printf("%lld\n",ans);
}