487-3279(重复的电话号码查询)(标签:优先队列,哈希表)

题目描述

企业喜欢用容易被记住的电话号码。让电话号码容易被记住的一个办法是将它写成一个容易记住的单词或者短语。例如,你需要给滑铁卢大学打电话时,可以拨打TUT-GLOP。有时,只将电话号码中部分数字拼写成单词。当你晚上回到酒店,可以通过拨打310-GINO来向Gino's订一份pizza。让电话号码容易被记住的另一个办法是以一种好记的方式对号码的数字进行分组。通过拨打必胜客的“三个十”号码3-10-10-10,你可以从他们那里订pizza。
电话号码的标准格式是七位十进制数,并在第三、第四位数字之间有一个连接符。电话拨号盘提供了从字母到数字的映射,映射关系如下:

A, B, 和C 映射到 2

D, E, 和F 映射到 3

G, H, 和I 映射到 4

J, K, 和L 映射到 5

M, N, 和O 映射到 6

P, R, 和S 映射到 7

T, U, 和V 映射到 8

W, X, 和Y 映射到 9

Q和Z没有映射到任何数字,连字符不需要拨号,可以任意添加和删除。 TUT-GLOP的标准格式是888-4567,310-GINO的标准格式是310-4466,3-10-10-10的标准格式是310-1010。

如果两个号码有相同的标准格式,那么他们就是等同的(相同的拨号)

你的公司正在为本地的公司编写一个电话号码薄。作为质量控制的一部分,你想要检查是否有两个和多个公司拥有相同的电话号码。

Input

输入的格式是,第一行是一个正整数,指定电话号码薄中号码的数量(最多100000)。余下的每行是一个电话号码。每个电话号码由数字,大写字母(除了Q和Z)以及连接符组成。每个电话号码中只会刚好有7个数字或者字母。

Output

对于每个出现重复的号码产生一行输出,输出是号码的标准格式紧跟一个空格然后是它的重复次数。如果存在多个重复的号码,则按照号码的字典升序输出。如果输入数据中没有重复的号码,输出一行:

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

解题思路

首先,经过对题目的分析可知,解决此题目的一般思路可划分模块如下:

Step1.输入号码薄中号码的数量(Input第一行)

Step2.若已输入的数量还未达到满容量,就输入一个未标准化(标准化的号码是指7位数字的号码,e.g.123-4567)的字符串;否则,跳到Step6

Step3.对上述输入的字符串进行标准化的转换

Step4.将标准化后的电话号码存储起来,保存到数据结构bank中,以便在后续继续输入其它号码时判断是否有重复号码

Step5.回到Step2

Step6.将bank中的号码进行一遍扫描,且记录多次重复出现的号码和出现次数到另一个数据结构preout中

Step7.对preout进行排序,并输出相应出现次数

观察以上步骤可发现,在Step2~Step5中,会对全部电话号码遍历一遍;在Step6中,扫描重复记录时遍历第二遍;并且在Step7中,我们对preout进行排序时,又将其中的一部分重复号码遍历了第三遍。所以,考虑是否可以在一次遍历中,将之前的三次遍历所做的事情一次性做完?于是,得到改进后的整体逻辑思路如下:

Step1.输入号码薄中号码的数量(Input第一行)

Step2.若已输入的数量还未达到满容量,就输入一个未标准化(标准化的号码是指7位数字的号码,e.g.123-4567)的字符串;否则,跳到Step6

Step3.对上述输入的字符串进行标准化的转换

Step4.将标准化后的电话号码存储起来,保存到数据结构bank中,若bank中已有这个号码,则标记这个号码出现的次数为上次出现的次数加一(++count);同时,若号码的出现次数等于2时,将号码插入到preout有序数据结构的适当位置,表示这个号码有重复,即将在程序的最后(Step6)顺序输出(数据结构bank和preout将在后文具体分析)

Step5.回到Step2

Step6.输出重复号码和相应出现次数

接下来就以上的步骤进行分步阐述:

Step1:(略)

Step2:(略)

Step3:对字符串的标准化包括去除连字符“-”和将非标准化号码中的字母转换为相应的数字。

去除连字符可对输入的非标准号码进行一次遍历去除。

转换为标准化号码时,会使用过多的switch-case语句,如下代码所示:

switch (s[i]){
case('A') {s[i] = ''; break;}
case('B') {s[i] = ''; break;}
case('C') {s[i] = ''; break;}
......
}

为避免使用过长代码,在转换时,可使用hash map存储字母到数字的转换关系。在C++ STL中,哈希映射的头文件为<unordered_map>。定义unordered_map类对象search_dict。并对hash map在构造函数中初始化。如下代码所示:

    Solution() {
/*初始化search_dict*/
search_dict[''] = '';
search_dict[''] = '';
search_dict['A'] = search_dict['B'] = search_dict['C'] = search_dict[''] = '';
search_dict['D'] = search_dict['E'] = search_dict['F'] = search_dict[''] = '';
search_dict['G'] = search_dict['H'] = search_dict['I'] = search_dict[''] = '';
search_dict['J'] = search_dict['K'] = search_dict['L'] = search_dict[''] = '';
search_dict['M'] = search_dict['N'] = search_dict['O'] = search_dict[''] = '';
search_dict['P'] = search_dict['R'] = search_dict['S'] = search_dict[''] = '';
search_dict['T'] = search_dict['U'] = search_dict['V'] = search_dict[''] = '';
search_dict['W'] = search_dict['X'] = search_dict['Y'] = search_dict[''] = '';
}

Step4:将转换后的标准化号码存储起来。因为每输入一个号码,要对此号码转换成标准号码后判断是否有重复,若在存储时选用简单的线性表结构,在查询重复操作时会产生O(n)的时间复杂度,因此,此处选用哈希表存储更高效。哈希表的key存储标准化后的号码,value存储号码出现的次数。在每次输入并转换成标准号码后,检查此号码是否在hash表中,若不在,则添加进去,并且value置为1。若已经存在,则value+1。

并同时进行判断,当value=2时,说明号码有重复,应当在程序末尾输出这个号码和对应的出现次数。所以应当新建一个数据结构进行重复号码的存储。value=2时,将号码追加进去。此处,又要做选择题了,应当用什么样的数据结构呢?这里可以选择可变长数组vector,对每个value=2的号码无序放入,等到程序末尾要输出时再排序;还有一个思路是用最小优先队列——即二叉堆来存储。二叉堆每次加入一个新元素或输出一个最小值的时间为log(N),但是连续加入或弹出N个元素的时间复杂度为N而非Nlog(N)。所以理论上要比变长数组存储最后输出时排序稍快。并且如这篇博文所述,vector容量不够的话,会重新开辟一块更大容量的空间,并且将原数组中的内容拷贝到新的空间。这就导致,变长数组会浪费更多的程序运行时间。所以,此处选用优先队列存储将要输出的重复号码值。

Step5:(略)

Step6:在优先队列不为空时,连续将队列头的元素pop出来并且查询hash表中出现的次数,输出到标准输出即可。

(2019-11-22更新)在输出排序时,也可以选用二叉搜索树作为数据结构。这样,在顺序输出时,直接对二叉搜索树做中序遍历即可得到顺序排列的号码。选用二叉搜索树与优先队列的摊还时间复杂度是一样的。

if (dup_nums.size() == ) {
cout << "No duplicates." << endl;
}
else {
while (dup_nums.size() != ) {
string number = dup_nums.top();
dup_nums.pop();
int dup_count = count_dict[number];
number.insert(number.begin() + , '-');
cout << number << ' ' << dup_count << endl;;
}
}

源代码

~~~Code~~~

C++ 11新标准实现POJ No.1002-487-3279的更多相关文章

  1. C++ 11新标准实现POJ No.1001-Exponentiation

    Exponentiation(高精度幂计算)(标签:链表,字符串,快速幂计算) 题目描述 对数值很大.精度很高的数进行高精度计算是一类十分常见的问题.比如,对国债进行计算就是属于这类问题. 现在要你解 ...

  2. C++ 11新标准实现POJ No.2195-GoingHome

    Going Home(回家)(标签:二部图,匈牙利算法,KM算法) 题目描述 在网格地图上,有n个男人和n个房屋. 在每个单位时间内,每个小人都可以水平或垂直移动一个单位步长到相邻的点. 对于每个小人 ...

  3. C++11新标准学习

    <深入理解C++11:C++11新特性解析与应用> <华章科技:深入理解C++11:C++11新特性解析与应用>一共8章:第1章从设计思维和应用范畴两个维度对C++11新标准中 ...

  4. C++11新标准:nullptr关键字

    一.nullptr的意义 1.NULL在C中的定义 #define NULL (void*)0 2.NULL在C++中的定义 #ifndef NULL #ifdef __cplusplus #defi ...

  5. C++11新标准:decltype关键字

    一.decltype意义 有时我们希望从表达式的类型推断出要定义的变量类型,但是不想用该表达式的值初始化变量(如果要初始化就用auto了).为了满足这一需求,C++11新标准引入了decltype类型 ...

  6. C++11新标准:auto关键字

    一.auto意义 编程时常常需要把表达式的值赋给变量,这就要求在声明变量的时候清楚地知道表达式的类型,然后要做到这一点并非那么容易.为了解决这个问题,C++11新标准引入了auto类型说明符,用它就能 ...

  7. c++11新标准for循环和lambda表达式

    :first-child { margin-top: 0px; } .markdown-preview:not([data-use-github-style]) h1, .markdown-previ ...

  8. 关注C++细节——C++11新标准之decltype的使用注意

    c++11新特性--decltype decltype是C++11加入的一个新的keyword,目的是选择并返回操作数的数据类型,重要的是,在此过程中编译器分析表达式并得到它的类型,却不实际计算表达式 ...

  9. 基于c++11新标准开发一个支持多线程高并发的网络库

    背景 新的c++11标准出后,c++语法得到了非常多的扩展,比起以往不论什么时候都要灵活和高效,提高了程序编码的效率,为软件开发者节省了不少的时间. 之前我也写过基于ACE的网络server框架,但A ...

随机推荐

  1. 设计模式 - 动态代理原理及模仿JDK Proxy 写一个属于自己的动态代理

    本篇文章代码内容较多,讲的可能会有些粗糙,大家可以选择性阅读. 本篇文章的目的是简单的分析动态代理的原理及模仿JDK Proxy手写一个动态代理以及对几种代理做一个总结. 对于代理模式的介绍和讲解,网 ...

  2. Java的数组的作业11月06日

    动手动脑 实验一:了解for循环得到棋盘结构 (1) 程序: import java.io.*; public class QiPan { //定义一个二维数组来充当棋盘 private String ...

  3. Python3 pygal 与 pygal_maps_world 绘制世界地图

    直接代码: import pygalfrom pygal_maps_world.i18n import COUNTRIES def word_country_map(): ""&q ...

  4. 面试官,不要再问我“Java GC垃圾回收机制”了

    Java GC垃圾回收几乎是面试必问的JVM问题之一,本篇文章带领大家了解Java GC的底层原理,图文并茂,突破学习及面试瓶颈. 楔子-JVM内存结构补充 在上篇<JVM之内存结构详解> ...

  5. [JZOJ5775]【NOIP2008模拟】农夫约的假期

    Description   在某国有一个叫农夫约的人,他养了很多羊,其中有两头名叫mm和hh,他们的歌声十分好听,被当地人称为“魔音”······  农夫约也有自己的假期呀!他要去海边度假,然而mm和 ...

  6. Spring整合Mybatis(IDEA版)

    本文适用于初学者: 该文主要教大家如何整合spring和mybatis,整合完成效果,可以从数据库中查询出学生信息: 完整的工程目录如下: 整合思路: 需要spring来管理数据源信息. 需要spri ...

  7. Python编程系列---使用装饰器传参+字典实现动态路由

    # 实现一个空路由表,利用装饰器将url和功能函数的对应关系自动存到这个字典中 router_dict = {} # 定义一个装饰器 # 再给一层函数定义,用来传入一个参数,这个参数就是访问的页面地址 ...

  8. python中requests模块-params与data的区别

    一般来说,params 在 get 请求中使用,data 在 post 请求中使用

  9. jmeter-操作mysql

    1.下载mysql驱动并放至如下目录:E:\soft\apache-jmeter-5.1.1\lib\ext 2.添加JDBC Connection Configuration(线程组-配置元件-JD ...

  10. 前后端hosts配置访问问题解决思路

    问题背景:前后端分离情况下后端开发测试需要配置hosts,有此问题的人员有RD,QA,PM,User 测试环境由于用户使用这种配置导致无法使用线上系统发起单据影响用户使用,同时让用户误以为系统出问题而 ...