Codeforces965E Short Code 【启发式合并】【堆】

本文介绍一种使用Trie树解决区分字符串集合问题的方法。通过构建Trie树并采用启发式合并策略,确保每条路径上的字符串均可唯一确定。算法实现过程中详细展示了插入操作、深度优先搜索以及节点合并的具体步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:

  给出总长度不超过1E+5的不重复字符串集,给每个字符串选一个前缀使得可以区分它。

题目分析:

  KAN出的DIV2难度一般不高,想升Ranting的可以试试。

  简单的树上启发式合并,建出Trie树,一开始每个字符串用自己表示,每次向上合并的时候选出堆中最大元素变成当前位置,特判一下有end的地方即可。

  证明也很简单,我们考虑一个根没被选的子树,若我们要使得这个子树的代价最小,那么我们一定要选择一个位置放到根上来,不难发现选择深度最大的点是会最小的。由于树的子结构的关系,这样向上归纳也是正确的。

代码:

  

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int sigma = 26;
 5 
 6 int n,num,len,root;
 7 char str[105000];
 8 struct trie{
 9     int end,sz,nxt[30];
10 }T[105000];
11 
12 int Num(char ch){return ch-'a';}
13 
14 void insert(int now,int pla){
15     if(pla == len) {T[now].sz++;T[now].end=1;return;}
16     int um = Num(str[pla]);
17     if(T[now].nxt[um]){
18     insert(T[now].nxt[um],pla+1);
19     }else{
20     num++; T[now].nxt[um] = num;
21     insert(T[now].nxt[um],pla+1);
22     }
23 }
24 
25 void dfs(int now){
26     for(int i=0;i<sigma;i++)
27     if(T[now].nxt[i]) dfs(T[now].nxt[i]),T[now].sz+=T[T[now].nxt[i]].sz;
28 }
29 
30 void read(){
31     scanf("%d",&n);
32     for(int i=1;i<=n;i++){
33     scanf("%s",str);
34     len = strlen(str);
35     insert(root,0);
36     }
37     dfs(root);
38 }
39 
40 int ans = 0;
41 int bel[102000];
42 priority_queue<int,vector<int>,less<int> > q[102000];
43 
44 int merge(int a,int b){
45     if(q[a].size()<q[b].size()){
46     while(!q[a].empty()){
47         int k = q[a].top();q[a].pop();
48         q[b].push(k);
49     }
50     return b;
51     }else{
52     while(!q[b].empty()){
53         int k = q[b].top();q[b].pop();
54         q[a].push(k);
55     }
56     return a;
57     }
58 }
59 
60 void dfs2(int now,int val){
61     int flag = false;
62     for(int i=0;i<sigma;i++){
63     if(T[now].nxt[i]) flag=true,dfs2(T[now].nxt[i],val+1);
64     }
65     if(!flag){bel[now]=++num;q[num].push(val);return;}
66     for(int i=0;i<sigma;i++){
67     if(T[now].nxt[i]){
68         if(bel[now]){bel[now]=merge(bel[now],bel[T[now].nxt[i]]);}
69         else bel[now] = bel[T[now].nxt[i]];
70     }
71     }
72     if(T[now].end){
73     q[bel[now]].push(val);
74     }else{
75     q[bel[now]].pop();
76     q[bel[now]].push(val);
77     }
78 }
79 
80 void work(){
81     num = 0;
82     for(int i=0;i<sigma;i++){
83     if(!T[root].nxt[i]) continue;
84     dfs2(T[root].nxt[i],1);
85     int hh = bel[T[root].nxt[i]];
86     while(!q[hh].empty()){
87         ans += q[hh].top();q[hh].pop();
88     }
89     }
90     printf("%d",ans);
91 }
92 
93 int main(){
94     read();
95     work();
96     return 0;
97 }

 

转载于:https://www.cnblogs.com/Menhera/p/8961932.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值