W
e
l
c
o
m
e
: )

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中关于根节点的处理不谋而合。

posted @ 2022-09-17 16:58  OberNotFound  阅读(43)  评论(0)    收藏  举报