您的位置:首页 > 其它

poj-3630 Phone List **

2011-10-06 17:39 337 查看
  Trie树入门题。不多说了, 先转一个人家的分析和代码, 自己的代码附在后面。

[转]:

方法一:trie树

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

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

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

代码如下:

1#include<iostream>
2using namespace std;
3int cases, count;
4int nodenum;
5
6struct node
7{
8    bool isExist;
9    node * branch[10];
10}Node[100000];
11
12class Trie
13{
14private:
15    node root;
16public:
17    Trie(){root = Node[0];}
18    bool insert(char num[])
19    {
20        node *location = &root;
21        int i = 0;
22        int len = strlen(num);
23        while(num[i])
24        {
25            if(i==len-1 && location->branch[num[i]-'0'] != NULL) //解决没有按照长度排序而存在的问题
26            {
27                return false;
28            }
29            if(location->branch[num[i]-'0']==NULL)//没有建立
30            {
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)
37            {
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()
49{
50    scanf("%d",&cases);
51    while(cases--)
52    {
53        nodenum = 1;
54        bool flag = true;
55        scanf("%d",&count);
56        char tel[11];
57        Trie t;
58        while(count--)
59        {
60            scanf("%s",tel);
61            if(!t.insert(tel))
62            {
63                flag = false;
64            }
65        }
66        if(flag)
67        {
68            printf("YES\n");
69        }
70        else
71        {
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树则是跳来跳去的,故会有一些开销吧。

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

代码如下:

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)
9{
10    return strcmp( (char*)a,(char*)b );
11}
12
13int main()
14{
15    scanf("%d",&cases);
16    while(cases--)
17    {
18        bool flag = true;
19        scanf("%d",&count);
20        for(i = 0; i < count; i++)
21        {
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++)
27        {
28            len1 = strlen(tel[i-1]);
29            len2 = strlen(tel[i]);
30            j = 0;
31            if(len1 <= len2)
32            {
33                while(tel[i-1][j] == tel[i][j] && j < len1)
34                {
35                    j++;
36                }
37                if(j == len1)
38                {
39                    flag = false;
40                }
41            }
42            if(!flag)
43            {
44                break;
45            }
46        }
47        if(flag)
48        {
49            printf("YES\n");
50        }
51        else
52        {
53            printf("NO\n");
54        }
55    }
56    return 0;
57}
58


[转] : http://www.cnblogs.com/cherish_yimi/archive/2009/10/12/1581795.html
__________________________________________________________________________________________

MyCode :

/*
* test-3.cpp
*
*  Created on: 2011-10-6
*
*/
#include <cstdio>
#include <cstring>
using namespace std;

const int maxN = 10000 + 5;
const int maxL = 10 + 3;
const int alphbetNum = 10;

int caseNum, n, nodeNum;
char telNum[maxL];

struct SData{
SData *next[alphbetNum];
int isExist;
};

SData trieTree[100000];
SData *trie;

SData* createTrie(){
SData *tmpTrie = &trieTree[0];
for(int i=0; i<alphbetNum; i++)
tmpTrie->next[i] = NULL;
tmpTrie->isExist = 0;
return tmpTrie;
}

int insert(char *newString){
SData *tmpTrie = trie;
char *nextChar = newString;
while(*nextChar != '\0'){
if(tmpTrie->isExist == 1)
return -1;

//关于这一步可以用负在下面的测试数据说明
if(strlen(nextChar) == 1 && tmpTrie->next[*nextChar - '0'] != NULL)
return -1;

if(tmpTrie->next[*nextChar - '0'] != NULL)
tmpTrie = tmpTrie->next[*nextChar - '0'];
else{
tmpTrie->next[*nextChar - '0'] = &trieTree[++nodeNum];
tmpTrie = tmpTrie->next[*nextChar - '0'];
for(int j=0; j<alphbetNum; j++)
tmpTrie->next[j] = NULL;
tmpTrie->isExist = 0;
}
nextChar++;
}
tmpTrie->isExist = 1;
return 1;
}

int main(){
scanf("%d", &caseNum);
while(caseNum--){
nodeNum = 0;
trie = createTrie();
bool flag = 1;

scanf("%d", &n);

for(int i=0; i<n; i++){
scanf("%s", telNum);

//如果flag=0则后面的不用再插入了
if(flag && insert(telNum) == -1)
flag = 0;
}

if(!flag) printf("NO\n");
else printf("YES\n");

}

return 0;
}


Input:

2
2
1
12
2
12
1

Output:
NO
NO
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: