OpenJudge 方便记忆的电话号码

目录

方便记忆的电话号码

要求: 

描述: 

输入: 

输出: 

样例输入: 

样例输出: 

问题分析: 

最终代码:

总结:


​​​​​​​

方便记忆的电话号码

要求: 

总时间限制: 2000ms

内存限制: 65536kB

描述: 

英文字母(除Q和Z外)和电话号码存在着对应关系,如下所示:

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

标准的电话号码格式是xxx-xxxx,其中x表示0-9中的一个数字。有时为了方便记忆电话号码,我们会将电话号码的数字转变为英文字母,如把263-7422记成America。有时,我们还加上“-”作为分隔符,如把449-6753记成Hi-World。当然,我们未必要将所有的数字都转变为字母,比如474-6635可以记成iPhone-5。

总之,一个方便记忆的电话号码由数字和除Q、Z外的英文字母组成,并且可以在任意位置插入任意多的“-”符号。

现在 ,我们有一个列表,记录着许多方便记忆的电话号码。不同的方便记忆的电话号码可能对应相同的标准号码,你的任务就是找出它们。

输入: 

第一行是一个正整数n(n <= 100000),表示列表中的电话号码数。
其后n行,每行是一个方便记忆的电话号码,它由数字和除Q、Z外的英文字母、“-”符号组成,其中数字和字母的总数一定为7,字符串总长度不超过200。

输出: 

输出包括若干行,每行包括一个标准电话号码(xxx-xxxx)以及它重复出现的次数k(k >= 2),中间用空格分隔。输出的标准电话号码需按照升序排序。

如果没有重复出现的标准电话号码,则输出一行“No duplicates.”。

样例输入: 

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

样例输出: 

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

问题分析: 

对于这个题目我们不难看出,大致分为两个步骤,1)将带有字母和多余“-”的电话号码转化成我们需要的标准电话号码;2)对所有的电话号码进行统计,找出重复的以及重复次数,并按顺序输出。而这个题目的关键在于列表中的电话号码数n是很大的(n\leqslant 100000)(n<=100000),这会对时间复杂度带来很大的挑战。

第一步是很简单的,但是为了节省时间,我们希望可以在输入的时候可以直接将格式转化成我们需要的形式,为此我们需要用到getchar(),来一个一个处理输入的字符。(getchar比cin.get快得多)

在这里有一个比较重要的优化,一般我们考虑的结果可能是这样:

char temp=getchar();
string _number;
while(temp!='\r'&&temp!='\n'){
   if(temp>='A'&&temp<='C') _number.push_back('2');
   else if(temp>='D'&&temp<='F') _number.push_back('3');
   else if(temp>='G'&&temp<='I') _number.push_back('4');
   else if(temp>='J'&&temp<='L') _number.push_back('5');
   else if(temp>='M'&&temp<='O') _number.push_back('6');
   else if(temp>='P'&&temp<='S') _number.push_back('7');
   else if(temp>='T'&&temp<='V') _number.push_back('8');
   else if(temp>='W'&&temp<='Y') _number.push_back('9');
   else if(temp!='-') _number.push_back(temp);
   temp=getchar();
}
_number.insert(3,"-");

但实际上这么多的if语句会带来较大的时间消耗,为此我们希望可以利用一种方式来取代这么多if,而switch在此处又并不适用,所以我们考虑利用一个const string来帮助我们实现这一点。         ( isdigit 函数可以用来判断char字符是否是0~9,很实用 )

const string Phone="22233344455566670778889990";
char temp=getchar();
string _number;
while(temp!='\r'&&temp!='\n'){
    if(temp!='-'){
        if (isdigit(temp)) _number.push_back(temp);
        else  _number.push_back(Phone[temp - 'A'] );
    }
    temp=getchar();
}
_number.insert(3,"-");

利用字母之间的位序关系来直接寻找所对应的数字,就显得十分方便快捷。

接下来我们考虑第二步,如何统计相同号码的重复次数,一种想法是先排序,再从小到大过一遍,看重复次数,这种想法是可行的;但我在这里讲的是利用map容器,可以直接对不同号码进行统计。当然,map容器的使用也涉及到了时间问题,经过实验发现,map的first元素是int类型是要比string类型更快的。所以我们可以修改一下第一部分,变成int类型存在map容器中。

int n;
    scanf("%d",&n);cin.get();
    map<int,int> Ans;
    for(int i=0;i<n;i++){
        char temp;
        int Number=0;
        temp=getchar();
        while(temp!='\r'&&temp!='\n'){
            if(temp!='-') {
                Number*=10;
                if (isdigit(temp)) Number += (temp - '0');
                else Number += (Phone[temp - 'A'] - '0');
            }
            temp=getchar();
        }
        Ans[Number]++;
    }

而第二部分利用map的特性可以直接解决:

bool flag=false;//判断是否有重复元素
    for(auto & An : Ans){//这里使用的是loop循环,也可以用Ans.begin()一位一位后移
        if(An.second>=2) {
            char Output[9];Output[8]='\0';//Output使用来输出的
            int Help=An.first;//An.first是不可更改的,所以需要另存
            for(int i=7;i>=0;i--){
                if(i==3){
                    Output[i]='-';continue;
                }
                Output[i]=Help%10+'0';
                Help/=10;
            }
            printf("%s %d\n", Output, An.second);
            flag=true;
        }
    }
    if(!flag) printf("No duplicates.\n");

 这样这个问题就最终解决了。

最终代码:

#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
const string Phone="22233344455566670778889990";
int main(){
    int n;
    scanf("%d",&n);getchar();
    map<int,int> Ans;
    for(int i=0;i<n;i++){
        char temp;
        int Number=0;
        temp=getchar();
        while(temp!='\r'&&temp!='\n'){
            if(temp!='-') {
                Number*=10;
                if (isdigit(temp)) Number += (temp - '0');
                else Number += (Phone[temp - 'A'] - '0');
            }
            temp=getchar();
        }
        Ans[Number]++;
    }
    bool flag=false;
    for(auto & An : Ans){
        if(An.second>=2) {
            char Output[9];Output[8]='\0';
            int Help=An.first;
            for(int i=7;i>=0;i--){
                if(i==3){
                    Output[i]='-';continue;
                }
                Output[i]=Help%10+'0';
                Help/=10;
            }
            printf("%s %d\n", Output, An.second);
            flag=true;
        }
    }
    if(!flag) printf("No duplicates.\n");
}

总结:

这个题目可以重温map的经典用法,难点就在于时间要求,scanf,printf,getchar就是省时三件套,中间利用const string 来简化也是十分重要的。如果这篇文章对您有所帮助的话,还请点赞关注哦! 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值