1.题目描述:

商业单位需要容易记忆的电话号码,有一些方法可以让电话号码变得更容易记忆。譬如,可以把电话号码写成单词或短语,如 MON-GLOP 可以代表滑铁卢大学的电话。有时仅仅是把号码的一部分写成单词,如打 310-GINO 便可向 GINO 比萨饼店定购比萨。另一种让电话号码容易记忆的方法是将数字用一种容易记的方式组合起来,譬如 3-10-10-10 也可以代表 GINO 比萨饼店。

电话号码的标准形式是七位十进制数字,在它的第三位和第四位之间用连字符连接(例如:666-1200)。电话的键盘提供了字符与数字之间的映射关系,如下所示:

2 A、B和C
3 D、E和F
4 G、H和I
5 J、K和L
6 M、N和O
7 P、R和S
8 T、U和V
9 W、X和Y

Q 和 Z 没有映射到键盘,而连字符不需要被拨打并且可以根据需要添加和删除。MON-GLOP 的标准形式是 666-4567,310-GINO 的标准形式是310-4466,3-10-10-10的标准形式也是 310-1010。

如果两个电话号码有相同的标准形式,那么这两个电话号码是相同的。

你所在的公司正在编辑一本当地商业单位的电话簿,作为质量控制流程的一部分,你需要确认在该电话簿中有没有错误的电话号码,以及有没有两个(或两个以上的)商业单位使用相同的电话号码。由于当地只使用了 3 和 6 两个区段,因此电话号码的第一个数字应当永远是 3 或者 6,如果出现了其它数字,就表示这个电话号码错了。此外,如果电话号码中出现了 Q 和 Z,也说明这个电话错了。

输入

一次输入为一个样例。每个号码一行,每行的字符不会超过 20 个。每次输入的数据可能会非常大,譬如超过 1,000,000 个电话号码。

你可以假设输入中可能会出现重复的电话号码不超过 1,500 个,每个号码重复的次数不超过 1000 次。

输出

输出包括两个部分,第一个部分是错误的电话号码,对于这些号码应当按照输入的顺序以原始的形式输出。在输出错误电话号码前输出“Error:”,随后输出这些号码,如果没有错误的电话号码,则输出“Not found.”。

第二部分是重复的电话号码,对每一个在电话簿中以任何形式出现一次以上的电话号码,生成一行输出。这一行应以标准形式给出电话号码,其后跟随一个空格,空格后跟随电话号码在电话簿中出现的次数。所有重复的电话号码输出行应以号码的升序排列(小号码在前)。在输出重复电话号码前输出“Duplication”,随后按照上述格式输出号码,如果在输入中没有重复的电话号码,则输出:“Not found.”。

注意

你所编写的程序以后可能会在一种特殊的嵌入式设备上运行,为了降低成本,这种设备使用的 CPU 不是很快、可用的 RAM 为 288K(跟 GBA 一样)且它没有磁盘设备因此不能使用文件作为数据的临时存储。

提示

请参考《编程珠玑》第一部分,若程序不能在规定的内存中运行,则不得分。

  测试输入 期待的输出 时间限制 内存限制 额外进程
测试用例 1 以文本方式显示

  1. 4873279↵
  2. ITS-EASY↵
  3. 666-4567↵
  4. 3-10-10-10↵
  5. 666-GLOP↵
  6. MON-GLOP↵
  7. 367-11-11↵
  8. 310-GINO↵
  9. F101010↵
  10. 666-1200↵
  11. -4-8-7-3-2-7-9↵
  12. 487-3279↵
以文本方式显示

  1. Error:↵
  2. 4873279↵
  3. ITS-EASY↵
  4. -4-8-7-3-2-7-9↵
  5. 487-3279↵
  6. Duplication:↵
  7. 310-1010 2↵
  8. 666-4567 3↵
1秒 512KB 0
测试用例 2 以文本方式显示

  1. 3456789↵
以文本方式显示

  1. Error:↵
  2. Not found.↵
  3. Duplication:↵
  4. Not found.↵
1秒 512KB 0

2.思路:

输入:给出大量的电话号码,我们需要判断是否错误以及是否重复,重复的话,按照号码大小顺序进行输出,如果错误的话,按照输入的顺序输出

存在字母到数字的映射,输出的时候需要我们输出标准型的电话号码

本题涉及到两个知识点

1.哈希表的应用

2.位运算实现哈希函数

首先本题有个难点就是我们限定了内存的大小,我们先从这里开始讲解

本题限制的内存大小事512k,但是我们可能存在很大的输入量,比如题中声明了我们会存在有1000000个电话号码

但是我们需要不断判断一个号码是不是重复,我们就必须要开辟很大的1000000的内存空间,大致进行计算,如果我们采用char类型来保存(相对int会节省空间)那么一个char一个字节,我们需要至少1000000byte==1000kb>>512kb

所以说,只用char来存储是不现实的

于是我根据我的思维导图来详细的阐释一下我的思维发展过程:

1.这道题我们要存储还要快速(不快速的话,1000000个数据很明显会超时),那么我该用什么数据结构存储比较合适呢

暑假我自学了数据结构,也了解了哈希表到底是什么,如果各位看官还不了解什么是哈希表,欢迎来参考我写的博客,里面详细的介绍了什么是哈希,以及怎么用哈希,哈西的核心思想是什么http://blog.csdn.net/ltyqljhwcm/article/details/52171743

我就简言概括一下什么是哈希表,哈希表又名散列表,核心思想就是通过哈希函数将目标关键字进行映射,获得新的存储地址,从而实现数据的快速的存取(理想时间复杂度是O(1),可以轻松解决我们的本题的超时的问题)

那么问题又来了,哈希实际上是以空间换取时间的一种数据结构,那么我们还是离不开大的数据存储,而且,我怎么设计哈希函数才能准确,平均而又不超出内存的限制下完成哈希表的构建,实现我们的随机存储呢?

2.哈希函数的设计着实让我伤脑筋

根据论文上的描述,有大量的哈希函数设计思路,比如除留取余,随机取中等等,但是本题我们的内存限制我们不能开很大的空间,但是不开很大的空间我们又不能将所有的情况覆盖,一旦出现输入的号码个数超过1000000并且又不重复,那不就GG了

所以说,我又想起了我假期看过的一本书叫做《编程之美》,是之美不是珠玑,我不知道出题人在哪看到的,我没有在编程珠玑上找到类似的解答,但是我在《编程之美》上受到了启发

编程之美的第一节是中国象棋将帅问题,大家有兴趣可以去了解一下,这道题和我们这道题有类似之处,两个题我们都限制了内存的大小,本题限制为288k还算比较宽容的,编程之美的问题限制内存是1个字节,但是为了保存两个将帅的位置信息,作者的处理方式是考虑到降水的坐标不会超过9的这个因素,将两个将帅的位置信息压缩到一个字节里面,因为9二进制不会超过4个二进制位,一个unsigned char有8位,我们一个字节存储绰绰有余

再回到我们这道题,在思考了两者的相似性之后,我决定尝试一种新的哈希函数的设计

我自己的思路是一个unsigned char有8位,我们有1000000个数据,那么只要(10000000/8,这里有个错误,下面的思考步骤中我会修改再压缩)的unsigned char数组就可以代表这一大量的内存,我们每一个二进制位有两个状态,0,1

0,代表该二进制(代表的数 反映射 的号码)没有出现过,1则代表出现过

利用位运算,我可以轻松处理将该为赋值为1,或者取出该为的值

位运算的核心代码如下:

int w=number/8;
     int x=number%8;
     if(((judge1[w]>>(7-x))&1)==1) return 1;   //判断该unsigned char的第x二进制位是否是1,
     else
     {
       judge1[w]=judge1[w]|(1<<(7-x));    //不是1,我们复制为1,代表该号码已经出现过
       return 0;
     }

我们的含义就是把电话号码处理成一个整数,然后利用位运算存储在这个unsigned char数组里面

3.具体的实现:

实现之后,我自信的点击了提交,结果显示超过内存

我就很不是理解了,回头再仔细看了下代码,发现了这个问题

我设置的哈希表的大小是1000000/8(没有设置成1000000/8是因为我但是脑抽的以为首位有很多数字,不知3,6,所以说拉高了一位,从一百万拉到了1000万)

我当时就开始思考如何再压缩,因为题意说明了首位只能是3,6,那么我们电话号码对应的整数也就只能是两种情况,我开辟两个大小是(1000000/8)的哈希表就可以将内存相对之前的压缩方案压缩至少75%的内存空间

这样子算一下,125000*2=250000个字节也就是大约250kb 是我们限制的内存空间的一半,通过八位一压缩,我成功的将1000kb的最原始的损耗内存的方式优化到250kb,轻松解决了速度和内存的我心里面是高兴的(然而事实证明高兴的太早了)

4.有了位运算实现哈希函数的哈希表这个数据结构,我开始了具体的实现这个代码

但是很是心累的,我WA了将近二十发,下面我将我所有的bug汇总一下,希望可以帮助到所有还在WA的道路上的同志们

1.小心不要直接将输入的字符串的首元素判断是不是3,6,或者和3,6相映射的字母来判断是不是error的情况,首字符可能是-,或者很多的--------

2.输出数组元素的时候不要轻易的用%s输出,因为有可能字符串型的数组中的电话号码存在0的情况,是中断输出的

3.排序函数中,我们的判断的思维逻辑一定要严谨

一定要分成大于等于小于所有的情况都不要理所当然的漏掉

4.字符判断是否相等的时候不要用strcmp拷贝复制的额时候不要用strcpy,理由同第二条

5.输入的字符里面不存在和题目要求的无关的字符,大家尽可能放心

6.最后一个用例中有一个特殊的情况就是情况1里面提及到的(不要问我怎么知道的,都是泪)

5.最后,位运算我给大家总结一波常用的和本题相关的一些基础用法作为结语

左移>>:二进制位整体左移,左端不齐的用0补齐

右移<<:二进制位整体右移,右端不齐的用0补齐

按位与&:二进制位逐位比较,同为1则该位为1,否则该位为0

按位或|:二进制位逐位比较,同为0则该位为0,否则该位为1

按位取反~:二进制位逐位取反

具体的常用用法:

1.取数字p的第k二进制位的具体数字:(p>>k)&1

2.为数字p的第k二进制位赋1:p|(1<<k)

3.将数字p的第k二进制位清零:p&~(1<<k)

4.p乘以2的k次方:p<<k

5.p除以2的k次方:p>>k

本题中这些位运算足够了,大家要是有兴趣百度即可

3.代码:

作为一名北理人,坚持贯彻不查重的政策,我只附上核心代码段:
int cmp(const void* a,const void* b)
{phone* x=(phone*)a;phone* y=(phone*)b;if(x->c > y->c) return 1;else if(x->c < y->c) return -1;else{for(int i=0;i<6;i++){if(x->code[i] > y->code[i]) return 1;else if(x->code[i]!=y->code[i]) return -1;else continue;}//return -1;}
}int creat()
{int c[22];memset(c,0,sizeof(c));int j=0;for(int i=0;i<strlen(input);i++){if(input[i]=='-') continue;else if(input[i]=='A'||input[i]=='B'||input[i]=='C') c[j++]=2;else if(input[i]=='D'||input[i]=='E'||input[i]=='F') c[j++]=3;else if(input[i]=='G'||input[i]=='H'||input[i]=='I') c[j++]=4;else if(input[i]=='J'||input[i]=='K'||input[i]=='L') c[j++]=5;else if(input[i]=='M'||input[i]=='N'||input[i]=='O') c[j++]=6;else if(input[i]=='P'||input[i]=='R'||input[i]=='S') c[j++]=7;else if(input[i]=='T'||input[i]=='U'||input[i]=='V') c[j++]=8;else if(input[i]=='W'||input[i]=='X'||input[i]=='Y') c[j++]=9;else if(input[i]>='0'&&input[i]<='9') c[j++]=(int)input[i]-48;}for(int i=1;i<j;i++) number=number*10+c[i];if(c[0]==3) return 1;   //3是1 else return -1;   //6是-1
}int findbit(int atom)
{if(atom==1){int w=number/8;int x=number%8;if(((judge1[w]>>(7-x))&1)==1) return 1;else{judge1[w]=judge1[w]|(1<<(7-x)); return 0;}}else{int w=number/8;int x=number%8;if(((judge2[w]>>(7-x))&1)==1) return 1;else{judge2[w]=judge2[w]|(1<<(7-x)); return 0;}}
}

2016BIT小学期——电话号码问题(哈希+位运算)相关推荐

  1. LeetCode 2032. 至少在两个数组中出现的值(哈希/位运算)

    文章目录 1. 题目 2. 解题 2.1 哈希查找 2.2 位运算 1. 题目 给你三个整数数组 nums1.nums2 和 nums3 ,请你构造并返回一个 不同 数组,且由 至少 在 两个 数组中 ...

  2. LeetCode 187. 重复的DNA序列(哈希/位运算)

    1. 题目 所有 DNA 都由一系列缩写为 A,C,G 和 T 的核苷酸组成,例如:"ACGAATTCCG". 在研究 DNA 时,识别 DNA 中的重复序列有时会对研究非常有帮助 ...

  3. 小技巧 ----- 通过二进制串的位运算进行一维指数型枚举

    一维指数型枚举 使用场景 给定n个水平方格 每个方格里面可以放1或0 那么所有存放的情况一共有2n2^n2n种 枚举出所有情况 二进制串的位运算结论 给定一个二进制串,如101011 从左到右可以规定 ...

  4. 小技巧 ----- 通过二进制串的位运算进行二维指数型枚举

    二维指数型枚举 题目: 给定4x4矩阵(每个元素为1或0) 枚举出所有情况,在矩阵元素为1的位置执行turn()方法 思路: 把4x4方格看作一个一维的水平长度为16的方格 即现在需要枚举一个长度为1 ...

  5. 北邮计算机学不学单片机,北邮小学期PC单片机.docx

    2013年小学期单片机设计实验报告 题目:乐动时钟 班级:2011211118 班内序号:12 姓名:杨帆 实验组号:35 实验摘要 本次实验我们制作的乐动闹钟总体上来说是一款懒人闹钟,可以随时通过电 ...

  6. 基于特征选择的局部敏感哈希位选择算法

    点击上方蓝字关注我们 基于特征选择的局部敏感哈希位选择算法 周文桦, 刘华文, 李恩慧 浙江师范大学数学与计算机科学学院,浙江 金华 321001 摘要:作为主流的信息检索方法,局部敏感哈希往往需要生 ...

  7. HNU小学期实训课设

    1,设计概述 由于社会的发展和科技的进步,人们对生活品质要求越来越高,而计算机时代的来临和物联网技术的发展也会给我们的生活带来巨大的变化,所以我希望通过物联网传感技术实现对闹钟的创意设计,从而改进人们 ...

  8. 嵌入式c语言小学期实验报告,小学期单片机实验报告

    北京邮电大学通信工程专业大二下暑假小学期单片机实验报告 欢迎下载 2012北邮小学期单片机实验报告 创新实验研究报告 基于单片机PIC32MX795F512L的 猜数字游戏 班 级: 学生姓名: 班内 ...

  9. 小学期实训-智慧电子时钟

    摘 要 项目选题为智慧电子时钟,相对于传统时钟,该智慧时钟有实时时钟.实时日期.光照与温度显示.闹钟设置.闹钟关闭密码设置功能.同时,闹钟需要满足一定的条件才能开启,通过光照传感器来实现对闹钟的开启条 ...

最新文章

  1. java封装对象实体类_Java 接口自动化系列--实体类之entity封装
  2. Response.Redirect、 Server.Transfer、Server.Execute三者区别
  3. 报告解读丨基于消费者洞察的鞋服品牌数字化营销新思路
  4. 硬核塔防策略手游《明日方舟》体验分析
  5. 【数据结构与算法】之深入解析“二叉树的中序遍历”的求解思路与算法示例
  6. azm335x 串口配置
  7. 异常信息java.lang.Object.wait(Native Method) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:
  8. 无人机官方网站_用上5G和无人机,你家高楼也可以做 体检
  9. git 打tag_团队开发中的 Git 实践
  10. IT男几时而立(上)
  11. MySQL latch小结
  12. 利用VS2010模仿QQ2011登陆界面
  13. 【Python】fastapi框架之Web部署机器学习模型
  14. AxGlyph矢量绘图软件
  15. 智慧医疗分支医院时钟系统(子母钟系统)
  16. SecureCRT键盘无法输入
  17. SSM + Ajax
  18. pc station v15 博图_博图的pcstation是做什么的?和wincc有什么区别?怎么使用?-工业支持中心-西门子中国...
  19. 猫眼电影诛仙评论爬取并进行数据分析
  20. R语言中的igraph包绘制网络图

热门文章

  1. tomcat 端口 8005 被 windows 系统服务占用导致启动闪退的问题
  2. 写代码不行,考研没考上,怎么办?
  3. Linux Control Groups (cgroups)
  4. web前端开发学什么?自学需要多久?
  5. 记录一次腾讯实习投递经历(一)
  6. android bitmap转drawable
  7. 畅通无阻的公式:乘员组从几乎破产变成了吸引500万游客的方式
  8. chrome开发者工具网络请求过滤
  9. C语言:输出n行由大写字母A开始构成的三角形字符阵列。
  10. Linux网络bug,Linux系统下的网络带宽测速