[POJ] #1003# 487-3279 : 桶排序/字典树(Trie树)/快速排序

一. 题目

487-3279

Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 274040   Accepted: 48891

Description

Businesses like to have memorable telephone numbers. One way to make a telephone number memorable is to have it spell a memorable word or phrase. For example, you can call the University of Waterloo by dialing the memorable TUT-GLOP. Sometimes only part of the number is used to spell a word. When you get back to your hotel tonight you can order a pizza from Gino‘s by dialing 310-GINO. Another way to make a telephone number memorable is to group the digits in a memorable way. You could order your pizza from Pizza Hut by calling their ``three tens‘‘ number 3-10-10-10.

The standard form of a telephone number is seven decimal digits with
a hyphen between the third and fourth digits (e.g. 888-1200). The
keypad of a phone supplies the mapping of letters to numbers, as
follows:

A, B, and C map to 2

D, E, and F map to 3

G, H, and I map to 4

J, K, and L map to 5

M, N, and O map to 6

P, R, and S map to 7

T, U, and V map to 8

W, X, and Y map to 9

There is no mapping for Q or Z. Hyphens are not dialed, and can be
added and removed as necessary. The standard form of TUT-GLOP is
888-4567, the standard form of 310-GINO is 310-4466, and the standard
form of 3-10-10-10 is 310-1010.

Two telephone numbers are equivalent if they have the same standard form. (They dial the same number.)

Your company is compiling a directory of telephone numbers from
local businesses. As part of the quality control process you want to
check that no two (or more) businesses in the directory have the same
telephone number.

Input

The
input will consist of one case. The first line of the input specifies
the number of telephone numbers in the directory (up to 100,000) as a
positive integer alone on the line. The remaining lines list the
telephone numbers in the directory, with each number alone on a line.
Each telephone number consists of a string composed of decimal digits,
uppercase letters (excluding Q and Z) and hyphens. Exactly seven of the
characters in the string will be digits or letters.

Output

Generate
a line of output for each telephone number that appears more than once
in any form. The line should give the telephone number in standard form,
followed by a space, followed by the number of times the telephone
number appears in the directory. Arrange the output lines by telephone
number in ascending lexicographical order. If there are no duplicates in
the input print the line:

No duplicates.

Sample Input

12
4873279
ITS-EASY
888-4567
3-10-10-10
888-GLOP
TUT-GLOP
967-11-11
310-GINO
F101010
888-1200
-4-8-7-3-2-7-9-
487-3279

Sample Output

310-1010 2
487-3279 4
888-4567 3

Source

East Central North America 1999

二. 题意

  1.电话簿上的电话号码为了方便记忆,并非全用数字表示,有些用字母表示或用 “ - ” 符号分割

  2.字母与数字之间存在一定的映射关系,“ - ” 没有实际意义

  3. 通过转换后得到统一标准的电话号码,其构成为"xxx-xxxx", 总共由八位字符(七位数字和一个"-")符号构成

  5. 现在给定一个电话清单,从中找出重复的电话号码

三. 分析

  1. 算法核心: 三种解决方法

    1.1 桶排序

    1.2 字典树(Trie树)

    1.3 快速排序

  2. 实现细节:

    2.1 桶排序

      2.1.1 桶的大小为七位数的上限(10000000)

      2.1.2 每个桶存储对应7位数的电话号码出现的次数

      2.1.3 将电话号码转换为7位数,递增对应该七位数的桶元素的值

      2.1.4 遍历所有桶,得到和输出大于1的所有桶元素

      2.1.5 小细节: 字符到数的转换,输出格式的正确转换

    2.2 字典树(Trie树)

      2.2.1 建立Trie树: 用静态数组存储每个node,  简化代码实现

      2.2.2 遍历所有输入电话号码,转换为数字,将该数字插入Trie树

      2.2.3 每插入一个完整电话号码,在其最后插入的node中设置两个标记:出现次数 和 完整电话号码的结束标记

      2.2.4 深度遍历(dfs)Trie树, 输出所有出现次数大于1的完整电话号码

      2.2.5 Trie树的所有child构成为从左到右(0 - 9),遍历的顺序也是如此,从而保证了电话号码的输出顺序也是从小到大

    2.3 快速排序

      2.3.1 用一个二维字符数组存储所有的电话号码

      2.3.2 对该数组进行快速排序

      2.3.3 遍历该有序数组,顺序输出大于1的所有电话号码

      2.3.4 个人实现算法的宗旨是尽量少用库,能实现的尽量自己实现

        2.3.4.1 对于此题,发现如果用自己实现的快速排序,发现会超时

         2.3.4.2 换成库的qsort则可以通过

        2.3.4.3 分析发现实际上库对于qsort做了很多的优化,并非基本的快排,有时间可以研究一下

        2.3.4.4 两个qsort优化的参考文档:

            http://blog.chinaunix.net/uid-25510439-id-275436.html

            http://blog.csdn.net/insistgogo/article/details/7785038

四. 题解

4.1 桶排序

 1 #include <stdio.h>
 2
 3 #define PHONE_NUMBER 256
 4 #define MAX_NUMBER 10000000
 5 #define BASE_NUMBER 1000000
 6 #define LENGTH_NUMBER 8
 7 #define MAP_SIZE (‘Z‘ - ‘A‘ + 1)
 8 int map_num[MAP_SIZE] = {2, 2, 2, 3, 3, 3, 4, 4, 4,
 9                          5, 5, 5, 6, 6, 6, 7, -1, 7,
10                          7, 8, 8, 8, 9, 9, 9, -1};
11 int bucket[MAX_NUMBER];
12
13 int main()
14 {
15     int i = 0, j = 0, count = 0, nums = 0;
16
17     scanf("%d\n", &count);
18     for (j = 0; j < count; j++) {
19         char phone_number[PHONE_NUMBER];
20         int tmp = 0;
21
22         scanf("%s\n", phone_number);
23
24         for (i = 0; phone_number[i] != ‘\0‘; i++) {
25             if (‘-‘ == phone_number[i]) continue;
26             tmp = tmp * 10 +
27                  ((phone_number[i] >= ‘A‘ && phone_number[i] <= ‘Z‘) ?
28                  map_num[phone_number[i] - ‘A‘] : phone_number[i] - ‘0‘);
29         }
30         bucket[tmp]++;
31     }
32
33     for (i = 0; i < MAX_NUMBER; i++) {
34         int b_tmp, base_number = BASE_NUMBER;
35
36         if (bucket[i] < 2) continue;
37
38         b_tmp = i;
39         nums++;
40
41         for (j = 0; j < LENGTH_NUMBER; j++) {
42             if (3 == j) { printf("-"); continue; }
43             printf("%d", b_tmp / base_number);
44             b_tmp %= base_number;
45             base_number /= 10;
46         }
47         printf(" %d\n", bucket[i]);
48     }
49
50     if (0 == nums) printf("No duplicates.\n");
51
52     return 0;
53 }

4.2 字典树(Trie树)

 1 #include <stdio.h>
 2
 3 #define BASE_NUMBER 1000000
 4 #define MAP_SIZE (‘Z‘ - ‘A‘ + 1)
 5 #define TRIE_CHILD_CNTS 10
 6 #define PHONE_NUMBER 256
 7 #define PHONE_NUMBER_LENGTH 7
 8 #define BASE_NUMBER 1000000
 9
10 int map_num[MAP_SIZE] = {2, 2, 2, 3, 3, 3, 4, 4, 4,
11                          5, 5, 5, 6, 6, 6, 7, -1, 7,
12                          7, 8, 8, 8, 9, 9, 9, -1};
13 int tree_index = 0;
14 int is_duplicated = 0;
15
16 typedef struct Trie {
17     int is_word;
18     int word_cnts;
19     struct Trie* childs[TRIE_CHILD_CNTS];
20 } trie_tree_t;
21
22 trie_tree_t trie_nodes[BASE_NUMBER];
23
24 void new_trie_tree(int index) {
25     int i;
26     trie_nodes[index].is_word = 0;
27     trie_nodes[index].word_cnts = 0;
28     for (i = 0; i < TRIE_CHILD_CNTS; i++) trie_nodes[index].childs[i] = NULL;
29 }
30
31 void add_trie_node(int num) {
32     int i, j, base_number = BASE_NUMBER;
33     trie_tree_t* tree = &trie_nodes[0];
34
35     for (i = 0; i < PHONE_NUMBER_LENGTH; i++) {
36         j = num / base_number;
37         num %= base_number;
38         base_number /= 10;
39         if (NULL == tree->childs[j]) {
40             new_trie_tree(++tree_index);
41             tree->childs[j] = &trie_nodes[tree_index];
42         }
43         tree = tree->childs[j];
44     }
45
46     tree->is_word = 1;
47     tree->word_cnts++;
48 }
49
50 void dfs(int phone_number[PHONE_NUMBER_LENGTH], int number_index, trie_tree_t *tree_p)
51 {
52     int i;
53     if (tree_p->is_word && 1 < tree_p->word_cnts) {
54         for (i = 0; i < PHONE_NUMBER_LENGTH; i++) {
55             if (3 == i) printf("-");
56
57             printf("%d", phone_number[i]);
58         }
59
60         printf(" %d\n", tree_p->word_cnts);
61         is_duplicated = 1;
62     }
63
64     for (i = 0; i < TRIE_CHILD_CNTS; i++) {
65         if (tree_p->childs[i] && number_index < PHONE_NUMBER_LENGTH) {
66             phone_number[number_index] = i;
67             dfs(phone_number, number_index + 1, tree_p->childs[i]);
68         }
69     }
70 }
71
72 int main()
73 {
74     int i = 0, j = 0, tmp = 0, count = 0;
75     int number[PHONE_NUMBER_LENGTH];
76
77     scanf("%d\n", &count);
78     for (j = 0; j < count; j++) {
79         char phone_number[PHONE_NUMBER];
80         int tmp = 0;
81         scanf("%s\n", phone_number);
82
83         for (i = 0; phone_number[i] != ‘\0‘; i++) {
84             if (‘-‘ == phone_number[i]) continue;
85             tmp = tmp * 10 +
86                  ((phone_number[i] >= ‘A‘ && phone_number[i] <= ‘Z‘) ?
87                  map_num[phone_number[i] - ‘A‘] : phone_number[i] - ‘0‘);
88         }
89
90         add_trie_node(tmp);
91     }
92
93     dfs(number, 0, &trie_nodes[0]);
94
95     if (0 == is_duplicated) printf("No duplicates.\n");
96
97     return 0;
98 }

4.3 快速排序

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4
  5 #define PHONE_NUMBER 256
  6 #define MAX_NUMBERS 100000
  7 #define LENGTH_NUMBER 9
  8 #define MAP_SIZE (‘Z‘ - ‘A‘ + 1)
  9 #define SEP_INDEX 3
 10
 11 #define CUSTOM 0
 12 #define USING_POINTER_FOR_COMPARE 0
 13
 14 char map_num[MAP_SIZE] = {‘2‘, ‘2‘, ‘2‘, ‘3‘, ‘3‘, ‘3‘, ‘4‘, ‘4‘, ‘4‘,
 15                          ‘5‘, ‘5‘, ‘5‘, ‘6‘, ‘6‘, ‘6‘, ‘7‘, ‘-1‘, ‘7‘,
 16                          ‘7‘, ‘8‘, ‘8‘, ‘8‘, ‘9‘, ‘9‘, ‘9‘, ‘-1‘};
 17
 18 char numbers[MAX_NUMBERS][LENGTH_NUMBER];
 19
 20 #if USING_POINTER_FOR_COMPARE
 21 char *p_numbers[MAX_NUMBERS];
 22 #endif
 23
 24 int is_duplicate = 0;
 25
 26 int mystrcmp(const char *str1, const char *str2)
 27 {
 28     while (*str1 == *str2) {
 29         if (*str1 == ‘\0‘) return 0;
 30         str1++; str2++;
 31     }
 32
 33     return *str1 - *str2;
 34 }
 35
 36 #if CUSTOM
 37
 38 #if USING_POINTER_FOR_COMPARE
 39
 40 void quicksort(char *A[], int left, int right, int(*func)(const char*, const char*)) {
 41     int i = left, j = right;
 42     char *t, *tmp;
 43     tmp = A[i];
 44
 45     if (i > j) return;
 46
 47     while (i != j) {
 48         while (func(tmp, A[j]) <= 0 && i < j) j--;
 49         while (func(A[i], tmp) <= 0 && i < j) i++;
 50
 51         if (i < j) {
 52             t = A[i];
 53             A[i] = A[j];
 54             A[j] = t;
 55         }
 56     }
 57     A[left] = A[i];
 58     A[i] = tmp;
 59
 60     quicksort(A, left, i - 1, func);
 61     quicksort(A, i + 1, right, func);
 62 }
 63
 64 #else
 65
 66 void mystrcpy(char *str1, const char *str2)
 67 {
 68     while (*str2 != ‘\0‘) {
 69         *str1 = *str2;
 70         str1++; str2++;
 71     }
 72
 73     *str1 = ‘\0‘;
 74 }
 75
 76 void quicksort(char A[][LENGTH_NUMBER], int left, int right, int(*func)(const char*, const char*)) {
 77     int i = left, j = right;
 78     char t[LENGTH_NUMBER], tmp[LENGTH_NUMBER];
 79     mystrcpy(tmp, A[i]);
 80
 81     if (i > j) return;
 82
 83     while (i != j) {
 84         while (func(tmp, A[j]) <= 0 && i < j) j--;
 85         while (func(A[i], tmp) <= 0 && i < j) i++;
 86
 87         if (i < j) {
 88             mystrcpy(t, A[i]);
 89             mystrcpy(A[i], A[j]);
 90             mystrcpy(A[j], t);
 91         }
 92     }
 93     mystrcpy(A[left], A[i]);
 94     mystrcpy(A[i], tmp);
 95
 96     quicksort(A, left, i - 1, func);
 97     quicksort(A, i + 1, right, func);
 98 }
 99
100 #endif
101
102 #endif
103
104 int sort_function(const void *a,const void *b)
105 {
106     return(strcmp((char*)a,(char*)b));
107 }
108
109 int main()
110 {
111     int i = 0, j = 0, count = 0, nums = 1, number_index = 0;
112
113     scanf("%d\n", &count);
114     for (j = 0; j < count; j++) {
115         char phone_number[PHONE_NUMBER];
116
117         scanf("%s\n", phone_number);
118
119         for (i = 0; phone_number[i] != ‘\0‘; i++) {
120             if (‘-‘ == phone_number[i]) continue;
121             if (SEP_INDEX == number_index) numbers[j][number_index++] = ‘-‘;
122             numbers[j][number_index++] = ((phone_number[i] >= ‘A‘ && phone_number[i] <= ‘Z‘) ?
123                                         map_num[phone_number[i] - ‘A‘] : phone_number[i]);
124         }
125         number_index = 0;
126
127 #if USING_POINTER_FOR_COMPARE
128         p_numbers[j] = numbers[j];
129 #endif
130
131     }
132
133 #if CUSTOM
134     quicksort(p_numbers, 0, count - 1, mystrcmp);
135
136 #if USING_POINTER_FOR_COMPARE
137
138     for (j = 0; j < count - 1; j++) {
139         if (0 == mystrcmp(p_numbers[j], p_numbers[j + 1])) {
140             is_duplicate = 1;
141             nums++;
142         } else {
143             if (1 < nums) printf("%s %d\n", p_numbers[j], nums);
144             nums  = 1;
145         }
146     }
147
148     if (1 < nums) printf("%s %d\n", p_numbers[j], nums);
149
150 #else
151
152     for (j = 0; j < count - 1; j++) {
153         if (0 == mystrcmp(numbers[j], numbers[j + 1])) {
154             is_duplicate = 1;
155             nums++;
156         } else {
157             if (1 < nums) printf("%s %d\n", numbers[j], nums);
158             nums  = 1;
159         }
160     }
161
162     if (1 < nums) printf("%s %d\n", numbers[j], nums);
163
164 #endif
165
166
167 #else
168
169     qsort(numbers, count, LENGTH_NUMBER, sort_function);
170
171     for (j = 0; j < count - 1; j++) {
172         if (0 == mystrcmp(numbers[j], numbers[j + 1])) {
173             is_duplicate = 1;
174             nums++;
175         } else {
176             if (1 < nums) printf("%s %d\n", numbers[j], nums);
177             nums  = 1;
178         }
179     }
180
181     if (1 < nums) printf("%s %d\n", numbers[j], nums);
182
183 #endif
184
185     if (0 == is_duplicate) printf("No duplicates.\n");
186
187     return 0;
188 }
时间: 2024-10-03 13:22:50

[POJ] #1003# 487-3279 : 桶排序/字典树(Trie树)/快速排序的相关文章

字典树Trie树

一.字典树 字典树--Trie树,又称为前缀树(Prefix Tree).单词查找树或键树,是一种多叉树结构. 上图是一棵Trie树,表示了关键字集合{"a", "to", "tea", "ted", "ten", "i", "in", "inn"} .从上图可以归纳出Trie树的基本性质: 1. 根节点不包含字符,除根节点外的每一个子节点都包含一

Atitit 常见的树形结构 红黑树 &#160;二叉树 &#160;&#160;B树 B+树 &#160;Trie树&#160;attilax理解与总结

Atitit 常见的树形结构 红黑树  二叉树   B树 B+树  Trie树 attilax理解与总结 1.1. 树形结构-- 一对多的关系1 1.2. 树的相关术语: 1 1.3. 常见的树形结构 红黑树  二叉树   B树 B+树  Trie树2 1.4. 满二叉树和完全二叉树..完全二叉树说明深度达到完全了.2 1.5. 属的逻辑表示 树形比奥死,括号表示,文氏图,凹镜法表示3 1.6. 二叉树是数据结构中一种重要的数据结构,也是树表家族最为基础的结构.3 1.6.1. 3.2 平衡二叉

【数据结构】字典树/Trie树/前缀树 - 字符串的统计、排序和保存

字典树 描述 字典树,又称单词查找树.Trie树.前缀树,是一种树形结构,是一种哈希树的变种. 典型应用是用于统计.排序和保存大量的字符串(但不仅限于字符串). 常见操作有插入和查找,删除操作少见. 性质 根节点不包含字符 除根节点外每一个节点都只包含一个字符 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串 每个节点的所有子节点包含的字符都不相同 优点 利用字符串的公共前缀来减少查询时间 最大限度地减少无谓的字符串比较 查询效率比哈希树高 自带字典序排序 直接判断重复,或者记

[转载]字典树(trie树)、后缀树

(1)字典树(Trie树) Trie是个简单但实用的数据结构,通常用于实现字典查询.我们做即时响应用户输入的AJAX搜索框时,就是Trie开始.本质上,Trie是一颗存储多个字符串的树.相邻节点间的边代表一个字符,这样树的每条分支代表一则子串,而树的叶节点则代表完整的字符串.和普通树不同的地方是,相同的字符串前缀共享同一条分支.还是例子最清楚.给出一组单词,inn, int, at, age, adv, ant, 我们可以得到下面的Trie: 可以看出: 每条边对应一个字母. 每个节点对应一项前

树-trie树

字典树(trie树) (图f) 字典树是一种以树形结构保存大量字符串.以便于字符串的统计和查找,经常被搜索引擎系统用于文本词频统计.它的优点是:利用字符串的公共前缀来节约存储空间,最大限度地减少无谓的字符串比较,查询效率比哈希表高.具有以下特点(图f):(1)根节点为空:(2)除根节点外,每个节点包含一个字符:(3)从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串.(4)每个字符串在建立字典树的过程中都要加上一个区分的结束符,避免某个短字符串正好是某个长字符串的前缀而淹没. T

【BZOJ3217】ALOEXT 替罪羊树+Trie树

[BZOJ3217]ALOEXT Description taorunz平时最喜欢的东西就是可移动存储器了……只要看到别人的可移动存储器,他总是用尽一切办法把它里面的东西弄到手. 突然有一天,taorunz来到了一个密室,里面放着一排可移动存储器,存储器里有非常珍贵的OI资料……不过比较特殊的是,每个存储器上都写着一个非负整数.taorunz很高兴,要把所有的存储器都拿走(taorunz的智商高达500,他一旦弄走了这里的所有存储器,在不久到来的AHOI和NOI中……你懂的).不过这时有一个声音

HiHo1014 : Trie树(Trie树模板题)

描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一本词典,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能对于每一个我给出的字符串,都在这个词典里面找到以这个字符串开头的所有单词呢?” 身经百战的小Ho答道:“怎么会不能呢!你每给我一个字符串,我就依次遍历词典里的所有单词,检查你给我的字符串是不是这个单词的前缀不就是了?” 小Hi笑道:“你啊,还是太年轻了!~假设这本词典里有10万个单

剑指Offer——Trie树(字典树)

剑指Offer--Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种.典型应用是统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高. Trie的核心思想是空间换时间.利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的. Trie树也有它的缺点,Trie树的内存消耗非常大.当然,或许用左儿子右兄弟的方法建树的话,可能会好点.可见,优

【转】B树、B-树、B+树、B*树、红黑树、 二叉排序树、trie树Double Array 字典查找树简介

B  树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 3.非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: B树的搜索,从根结点开始,如果查询的关键字与结点的关键字相等,那么就命中:否则,如果查询关键字比结点关键字小,就进入左儿子:如果比结点关键字大,就进入右儿子:如果左儿子或右儿子的指针为空,则报告找不到相应的关键字: 如果B树的所有非叶子结点的左右子树的结点数目均保持差不多(平衡),那么B树的搜索性