2022实战练习X 解题报告
2022实战练习 X
B. 选ID
思路
问题中要求LCP(最长公共前缀),考虑用Trie求解
更具体地,对姓名建字典树,比如:

每个节点中多维护了一个数字,它表示从根节点到此节点的路径的出现次数,换言之,每个前缀在所有姓名的前缀中出现的次数,此例中的姓名为:
ab
ac
bd
维护这个信息的原因是有可能出现多个ID串前缀完全相同,这时查找这些ID串的路径可能高度重合,但仅当这串前缀的出现次数足够多,才能对答案产生贡献,同时因为题目中要求姓名和ID一一对应,因此匹配后还需将路径上的该数字\(-1\)
此时每一个ID串的答案即为从根到第一个不同位或第一个出现次数为\(0\)的路径的长度
AC代码
#include <bits/stdc++.h>
#define ifile(x) freopen(x,"r",stdin)
#define ofile(x) freopen(x,"w",stdout)
#define iofile(x,y) freopen(x,"r",stdin),freopen(y,"w",stdout)
using namespace std;
struct node
{
int cnt=0;
node *nxt[26]={nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr};
};
int len;
node *root=new node;
vector <node*> ptr={root};
char S[100005];
void insert(int pos,node *now)
{
if(pos==len)
{
return;
}
if(now->nxt[S[pos]-'a']==nullptr)
{
now->nxt[S[pos]-'a']=new node;
ptr.push_back(now->nxt[S[pos]-'a']);
}
++now->nxt[S[pos]-'a']->cnt;//添加子节点的出现数量
insert(pos+1,now->nxt[S[pos]-'a']);
}
int ans=0;
void query(int pos,node *now)
{
if(pos==len)
{
return;
}
if(now->nxt[S[pos]-'a']==nullptr)
{
return;
}
if(now->nxt[S[pos]-'a']->cnt)
{
--now->nxt[S[pos]-'a']->cnt;//减少子节点的可用出现数量
++ans;
}
query(pos+1,now->nxt[S[pos]-'a']);
}
int main()
{
iofile("choose.in","choose.out");
int n;
scanf("%d",&n);
for(int i=0;i<n;++i)
{
scanf("%s",S);
len=strlen(S);
insert(0,root);
}
for(int i=0;i<n;++i)
{
scanf("%s",S);
len=strlen(S);
query(0,root);
}
printf("%d\n",ans);
for(node *p:ptr)
{
delete p;
}
return 0;
}
Tips
实际程序中无需保存根节点的数字,毕竟将代表边的路径反映到点上,使用左开右闭的方式也很直观,即忽略根节点,保留终点节点,这也与Trie中关于根节点的处理不谋而合。

浙公网安备 33010602011771号