杭电1671————字典树基础(重要)


Phone List

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 10980    Accepted Submission(s): 3796


Problem 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

题意是很清楚的,典型的字典树。
不过这个题目我一开始很天真的就按照最基本的方式做的,事实证明我naive了...
我的传统做法有一个漏洞,就是如果当前字符串比字典树中任意一个字符串都要长的时候,是会出错的。
当前字符串可以是已经建立的字典树的前缀,也有可能是这个字典树中的某一个字符串是当前字符串的前缀,我的传统做法只考虑了第一种情况!
所以要改一下策略。
在CreatTrie函数中,这么写(参考Tanky Woo)
void CreatTrie(char *str) 
{
        int id = str[i]-'0';
        if(p->next[id] == NULL)
        {
            q = (Trie *)malloc(sizeof(Trie));
            q->v = 1;
            for(int j=0; j<10; ++j)
                q->next[j] = NULL;
            p->next[id] = q;
            p = p->next[id];
        }
        else
        {
            p = p->next[id];
            p->v++;
        }
    }
    p->v = -1;//当做末尾标志 
}

注意这个p-> = -1是重要的。
什么意思呢?比如说,单词acm,那么m的val是-1
判断acmpdey这个单词的时候,这个单词很明显比acm长,即acm应该是acmpdey的前缀才对。
那么find函数中可以这么写
<span style="font-size:14px;">int FindTrie(char *str)
{
    int len = strlen(str);
    Trie *p = root;
    for(int i = 0 ; i< len ;  ++i)
    { 
        int id = str[i]-'0';
        p = p->next[id];
        if(p == NULL)
            return false;
        if(p->v == -1)//如果当前单词是比较长的,能遍历到p->v == -1的位置说明前边的字符都是一样的
            return true;
    }
    return true;//单前单词比较短,且查找完毕 
}</span>
对比一下传统的写法:
<span style="font-size:14px;">int FindTrie(char *str)
{
    int len = strlen(str);
    Trie *p = root;
    for(int i = 0 ; i< len ;  ++i)
    { 
        int id = str[i]-'0';
        p = p->next[id];
        if(p == NULL)
            return false;
    }
    return true;
}</span>

(这种做法如果碰到上述acm和acmpdey的情况是判断不出来的)这样我们就把两种情况全考虑进去了(如果不明白可以看后面的完整代码)
附代码: (328MS)
<span style="font-size:14px;"><span style="font-family:Microsoft YaHei;font-size:14px;color:#333333;">#include <iostream>
#include <cstdio>
#include <cstring>
#include <malloc.h>
using namespace std;
 
typedef struct Trie{
    int v;
    Trie *next[10];
}Trie;
 
Trie *root;
 
void CreateTrie(char *str)
{
    int len = strlen(str);
    Trie *p = root, *q;
    for(int i = 0 ; i < len ; ++i)
    {
        int id = str[i]-'0';
        if(p->next[id] == NULL)
        {
            q = (Trie *)malloc(sizeof(Trie));
            q->v = 1;
            for(int j=0; j<10; ++j)
                q->next[j] = NULL;
            p->next[id] = q;
            p = p->next[id];
        }
        else
        {
            p = p->next[id];
            p->v++;
        }
    }
    p->v = -1;//当做末尾标志 
}
 
int FindTrie(char *str)
{
    int len = strlen(str);
    Trie *p = root;
    for(int i = 0 ; i< len ;  ++i)
    { 
        int id = str[i]-'0';
        p = p->next[id];
        if(p == NULL)
            return 0;
        if(p->v == -1)//这两个地方很重要! 
            return -1;
    }
    return -1;//-1是末尾标志,表示全部查询完毕 
}
 
int deal(Trie* T)
{//这是把T清空,不清空字典树会内存泄露 
    if(T==NULL)
        return 0;
    for(int i = 0 ; i < 10 ; i++)
    {
        if(T->next[i] != NULL)
            deal(T->next[i]);
    }
    free(T);
    return 0;
}
 
int main()
{
    char str[15];
    int nCases, nNum;
    bool flag;
    scanf("%d", &nCases);
    while(nCases--)
    {
        flag = 0;
        root = (Trie *) malloc (sizeof(Trie));
        for(int i = 0; i < 10 ; ++i)
            root->next[i] = NULL;
        scanf("%d", &nNum);
        for(int i = 0 ; i < nNum ; ++i)
        {
            scanf("%s", str);
            if(FindTrie(str) == -1)
                flag = 1;
            if(flag)   
                continue;
            CreateTrie(str);
        }
        if(flag)
            printf("NO\n");
        else
            printf("YES\n");
        deal(root);
    }
    return 0;
}</span>




posted @ 2014-08-28 17:25  SixDayCoder  阅读(225)  评论(0编辑  收藏  举报