PKU POJ 3630解题报告(trie树)

文章作者:yx_th000   文章来源:Cherish_yimi (http://www.cnblogs.com/cherish_yimi/转载请注明,谢谢合作。

Trie学习简单的trie树详解

Phone List
Time Limit: 1000MSMemory Limit: 65536K
Total Submissions: 5512Accepted: 1770

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:

  • Emergency 911
  • Alice 97 625 999
  • 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

方法一:trie

有了上面学习的思考与总结,3630trie树本以为可以水过,可是学习和做题终究是两回事,我很快写出trie树,然后提交,超时了。

后来受discuss提示,我大致计算了一下本题trie树的复杂度,号码个数10000,长度10,树的宽度大概有10000,所以总的节点数大概就有100,000级,即要进行十万次new的操作,确实时间耗费很多,估计这样题目的用时要有1秒到2秒左右的样子。

于是为了纯粹的做题,我将new操作省掉,改为提前申请一个buffer空间,就ac了,时间变为125ms了,不过这样确实挺耗空间的,没办法,为了做题只能用空间换时间。

代码如下:

ContractedBlock.gif ExpandedBlockStart.gif Code
 1#include<iostream>
 2using namespace std;
 3int cases, count;
 4int nodenum;
 5
 6struct node
 7ExpandedBlockStart.gifContractedBlock.gif{
 8    bool isExist;
 9    node * branch[10];
10}
Node[100000];
11
12class Trie
13ExpandedBlockStart.gifContractedBlock.gif{
14private:
15    node root;
16public:
17ExpandedSubBlockStart.gifContractedSubBlock.gif    Trie(){root = Node[0];}
18    bool insert(char num[])
19ExpandedSubBlockStart.gifContractedSubBlock.gif    {
20        node *location = &root;
21        int i = 0;
22        int len = strlen(num);
23        while(num[i])
24ExpandedSubBlockStart.gifContractedSubBlock.gif        {
25            if(i==len-1 && location->branch[num[i]-'0'!= NULL) //解决没有按照长度排序而存在的问题
26ExpandedSubBlockStart.gifContractedSubBlock.gif            {
27                return false;
28            }

29            if(location->branch[num[i]-'0']==NULL)//没有建立
30ExpandedSubBlockStart.gifContractedSubBlock.gif            {
31                location->branch[num[i]-'0'= &Node[nodenum];
32                Node[nodenum].isExist = false;
33                memset(Node[nodenum].branch,NULL,sizeof(Node[nodenum].branch));
34                nodenum++;
35            }

36            if(location->branch[num[i]-'0']->isExist == true)
37ExpandedSubBlockStart.gifContractedSubBlock.gif            {
38                return false;
39            }

40            location = location->branch[num[i]-'0'];
41            i++;
42        }

43        location->isExist = true;
44        return true;
45    }

46}
;
47
48int main()
49ExpandedBlockStart.gifContractedBlock.gif{
50    scanf("%d",&cases);
51    while(cases--)
52ExpandedSubBlockStart.gifContractedSubBlock.gif    {
53        nodenum = 1;
54        bool flag = true;
55        scanf("%d",&count);
56        char tel[11];
57        Trie t;
58        while(count--)
59ExpandedSubBlockStart.gifContractedSubBlock.gif        {
60            scanf("%s",tel);
61            if(!t.insert(tel))
62ExpandedSubBlockStart.gifContractedSubBlock.gif            {
63                flag = false;
64            }

65        }

66        if(flag)
67ExpandedSubBlockStart.gifContractedSubBlock.gif        {
68            printf("YES\n");
69        }

70        else
71ExpandedSubBlockStart.gifContractedSubBlock.gif        {
72            printf("NO\n");
73        }

74    }

75    return 0;
76}

77

方法二:

    转成数字存储比较,这样的话用long整形就可以,然用除法+取余的方法核对是否是某个数字的前缀,但是这种方法的复杂度显然是O(n^2)呀,所以就不尝试了。

方法三:

受大雄提示,可以使用字符串排序比较来做,因为通过排序,前缀子串肯定是与父串挨着的,嘿嘿,这样做,思路简单、代码量少,易理解啊,所以很快ac,下面分析一下复杂度。
    
理论上使用trie的平均复杂度应该是n*len;其中,len是号码的平均长度,n是号码的个数。使用数组进行字符比较,理论上的复杂度有n*len+logn,排序为logn,然后查询是否存在前缀子串是n*len。所以后者应该时间稍微多一点,提交后果然,耗时188ms

另外值得一提的是使用数组比较的方法有个好处,那就是地址都是连续的,cpu在寻址时会非常快,而用链式结构(即指针),包括使用数组型的trie树则是跳来跳去的,故会有一些开销吧。

呵呵,我所崇拜的排序又一次派上用场了。

代码如下:

 

ContractedBlock.gif ExpandedBlockStart.gif Code
 1#include<iostream>
 2using namespace std;
 3
 4int cases, count;
 5char tel[10005][11];
 6int i, j;
 7
 8int cmp(const void *a, const void *b)
 9ExpandedBlockStart.gifContractedBlock.gif{
10    return strcmp( (char*)a,(char*)b );
11}

12
13int main()
14ExpandedBlockStart.gifContractedBlock.gif{
15    scanf("%d",&cases);
16    while(cases--)
17ExpandedSubBlockStart.gifContractedSubBlock.gif    {
18        bool flag = true;
19        scanf("%d",&count);
20        for(i = 0; i < count; i++)
21ExpandedSubBlockStart.gifContractedSubBlock.gif        {
22            scanf("%s",tel[i]);
23        }

24        qsort(tel,count,sizeof(char)*11,cmp);
25        int len1, len2;
26        for(i = 1; i < count; i++)
27ExpandedSubBlockStart.gifContractedSubBlock.gif        {
28            len1 = strlen(tel[i-1]);
29            len2 = strlen(tel[i]);
30            j = 0;
31            if(len1 <= len2)
32ExpandedSubBlockStart.gifContractedSubBlock.gif            {
33                while(tel[i-1][j] == tel[i][j] && j < len1)
34ExpandedSubBlockStart.gifContractedSubBlock.gif                {
35                    j++;
36                }

37                if(j == len1)
38ExpandedSubBlockStart.gifContractedSubBlock.gif                {
39                    flag = false;
40                }

41            }

42            if(!flag)
43ExpandedSubBlockStart.gifContractedSubBlock.gif            {
44                break;
45            }

46        }

47        if(flag)
48ExpandedSubBlockStart.gifContractedSubBlock.gif        {
49            printf("YES\n");
50        }

51        else
52ExpandedSubBlockStart.gifContractedSubBlock.gif        {
53            printf("NO\n");
54        }

55    }

56    return 0;
57}

58

 

转载于:https://www.cnblogs.com/cherish_yimi/archive/2009/10/12/1581795.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值