HDU 1671 Phone List(字典树/巧解)

本文介绍了一种算法问题,即判断一系列电话号码是否满足没有一个号码是另一个号码的前缀。文章提供了三种解决方法,包括使用字典树、在构造过程中进行判断以及一种巧妙的方法。并给出了具体的代码实现。

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

原题

  Phone List

Time Limit: 1000MS Memory Limit: 32768KB 64bit IO Format: %I64d & %I64u

Description

Given a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another. Let’s say the phone catalogue listed these numbers:
1. Emergency 911
2. Alice 97 625 999
3. Bob 91 12 54 26
In this case, it’s not possible to call Bob, because the central would direct your call to the emergency line as soon as you had dialled the first three digits of Bob’s phone number. So this list would not be consistent.

Input

The first line of input gives a single integer, 1 <= t <= 40, the number of test cases. Each test case starts with n, the number of phone numbers, on a separate line, 1 <= n <= 10000. Then follows n lines with one unique phone number on each line. A phone number is a sequence of at most ten digits.

Output

For each test case, output “YES” if the list is consistent, or “NO” otherwise.

Sample Input

2
3
911
97625999
91125426
5
113
12340
123440
12345
98346

Sample Output

NO
YES

题意

与前缀编码有一点类似,本题是要判断给出的号码是否为”前缀号码“(即任何一个号码都不能是其他号码的前缀)。

涉及知识及算法

字典树、前缀编码详见百度百科

法1:构造字典树,标记号码的末尾结点,完成构造后在树中搜索判断是否在叶子结点之外还有标记。

法2:在构造的同时判断。对“911 91125426”判断后者插入时路径上是否有标记,对“91125426 911”判断后者是否生成新结点。

法3:(巧解) 首先将所有的号码当做 long long 型存入数组,读完后进行排序,再从小到大读出每个数,并用map进行标记以示这个数出现过,然后在对这个数不断进行除10操作,判断除10后的数有没有被标记,不过WA了,后来想想看应该是有些号码前面是0,这样就不行了,不过后来一想可以将所有的数前面都加个1不就解决了。。。。

——详见博主fszxwfy http://fszxwfy.blog.163.com/blog/static/44019308201282484625551/

在此就只贴上法2的代码了

代码

#include <iostream>
#include <cstdio>
#include <malloc.h>
#include <cstdlib>
#include <cstring>
using namespace std;
int flag,flag2;
typedef struct Trie_node
{
    bool exist;
    struct Trie_node* next[10];
}TrieNode,*Trie;

TrieNode* createTrieNode()
{
    TrieNode* node=(TrieNode*)malloc(sizeof(TrieNode));
    node->exist=false;
    memset(node->next,0,sizeof(node->next));
    return node;
}

void Trie_insert(Trie root,char* word)
{
    Trie node=root;flag2=0;
    char *p=word;
    int id;
    while(*p)
    {
        id=*p-'0';
        if(node->next[id]!=NULL&&node->next[id]->exist)       //法2的第一个判断
        {
            flag=1;
        }
        if(node->next[id]==NULL)                       
        {
            node->next[id]=createTrieNode(); flag2=1;       //如果未生成新结点,则flag2=0
        }
        node=node->next[id];
        ++p;
    }
    if(!flag2) flag=1;         //第二个判断
    node->exist=true;
}

void Clear(Trie a)              //释放内存
{
    if(a==NULL) return;
    else
    {
        for(int i=0;i<10;i++)
        {
            Clear(a->next[i]);
        }
    }
    free(a);
}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    char str[11];
    int kase;
    cin>>kase;
    while(kase--)
    {
        Trie root=createTrieNode();
        flag=0;
        int Count;
        cin>>Count;
        for(int i=0;i<Count;i++)
        {
            scanf("%s",str);
            Trie_insert(root,str);
            //cout<<"insert success"<<endl;
        }
        if(flag) cout<<"NO"<<endl;
        else cout<<"YES"<<endl;
        Clear(root);
        //cout<<"Clear success!"<<endl;
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值