大卫david的专栏

每天进步一点点

POJ1002 487-3279

POJ上的一道题目:企业想拥有令人难忘的电话号码。要一个电话号码让人容易记住,方法之一是号码里面有一个难忘的单词或词组拼写。例如,您可以拨打TUT- GLOP拨打沃特卢大学。有时电话号码只有一部分是单词拼写。当你回到你的酒店,今晚你可以通过拨打310 - GINO吉诺订购比萨饼。另一种方法,设计的电话号码是一个难忘的方式分组数字。你可以通过拨打必胜客" 3个10 "的号码3-10-10-10 来订购比萨.

  一个电话号码的标准形式是7个十进制数字用连字符之间的第三和第四位数字(如888-1200 ).一个电话的小键盘提供字母数字的映射,如下所示:
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 。两个电话号码是等价的,如果它们具有相同的标准形式.(他们拨打同一个号码。 )你的公司正在编制当地企业的电话号码目录。作为质量控制过程的一部分,你要检查,没有两个(或以上)的企业目录中的有相同的电话号码.
  输入输入将包括一个案例。输入的第一行中指定的目录中的电话号码的数目(最多10万)作为单独行上的一个正整数.其余各行列出在目录中的电话号码,在单独一行的每个数字。每个电话号码的十进制数字,大写字母(不包括Q和Z )和连字符组成的字符串组成. 整整7个数字或字母的字符串中的字符.

产量
  生成一行输出为每个电话号码,任何形式多次出现。该行应得到的标准形式的电话号码,后跟一个空格,其次是在电话号码出现在目录的次数。电话号码升字典顺序排列输出线。如果没有重复在输入打印线:

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


  这道题目解法多种多样,难点在于时间复杂度的控制,花了好长时间在这道题目上,下面说说我的经验;
  刚开始我想定义一个数据结构来存储,str字符数组用来存储每一个电话号码(去掉了'-'字符);identy用于存储字符数组的第一个字符,用于排序;icount用来统计每个相同号码的个数,用一个for循环实现起来并不复杂,可是程序一直是TLE(Time Limit Exceeded),查阅了一些资料,不服气自己的程序就是不能通过这道题,一直在修改修改再修改,后来发现,我从一开始就错了,我错的是我把电话号码当成一个字符数组来处理,还创建了结构体来存储,假设有10万个电话号码,还要排序,那时间复杂度就很高了,而且在循环体里面使用了strcmp,strcpy等库函数,花在这上面的时间,灰常多,继续在这上面修改,也不会有多好的效果的,在论坛上得到了大神的启发,转战用数字才存放电话号码,int类型的足矣.

typedef struct
{
    int identy;
    char str[9];
    int icount;
}Phone;
于是,不采用结构体,直接用整型数组,这样不需要通过什么strcmp,strcpy函数来判断大小和赋值,也能直接用数组存储数据来排序,这样一来,时间复杂度将会大大减小。完整代码是:

#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 100000
int cmp(const void *a, const void *b)
{
    int *x = (int *)a;
    int *y = (int *)b;
    return (*x) - (*y);
}
int main()
{
    int cnt = 0;   //存放电话号码个数
    int iter = 0;
    int Noduplicates = 0;  //有无重复号码的标志,为0则表示无重复
    int Sum = 0;
    int icount = 0;
    char ch;      // 接收键盘输入字符
    int *PHONE = (int *)malloc(sizeof(int) * (MAXSIZE + 10));
    scanf("%d",&cnt);
    getchar();
    
    //获取所有电话号码,转换为整型数据
    for(iter = 0; iter < cnt; iter++)
    {
        Sum = 0;
        ch = getchar();
        while(ch != '\n')
        {
            if(ch >= '0' && ch <= '9')
            {
                Sum = Sum * 10 + ch - '0' ;
            }
            else if(ch >= 'A' && ch <= 'P')
            {
                Sum = Sum * 10 + (ch - 65) / 3 + 2 ;
            }
            else if(ch >= 'R' && ch <= 'Y')
            {
                Sum = Sum * 10 + (ch - 66) / 3 + 2 ;
            }
            ch = getchar();
        }
        PHONE[iter] = Sum;
    }
    
    //用快排对电话号码排序
    qsort(PHONE, cnt, sizeof(int), cmp);
    
    //输出排好顺序的号码
    for(iter = 0, icount = 1; iter < cnt - 1 ; iter++)
    {
        icount = 1;
        //统计重复号码个数
        while(PHONE[iter] == PHONE[iter + 1])
        {
            iter++;
            icount++;
        }
        if(icount > 1)
        {
            Noduplicates = 1;
            printf("%03d-%04d %d\n",PHONE[iter] / 10000 ,PHONE[iter] % 10000 ,icount);
        }
    }
    if(Noduplicates == 0)
    {
        printf("No duplicates.\n");
    }
    getchar();
    return 0;
}
注: QZ 是不在映射里面的!还有就是输出格式,比如号码"000-0001"不应该输出"   -    1"(前导 0 的问题)

给几组测试用例:
 用例1:
2
---3333--3-3-3--
-3333333

输出:
333-3333 2

用例2:
4
0000000
0010001
0000000
0010001

输出:
000-0000 2
001-0001 2

要是这样你还不能AC,再看看下面几点建议:
1、数据量大时候果然要注意输入的时间,用cin超时,改用gets就AC了;
2、是不是原来是 "No duplicates." 少写了一个句号,或者后面多了一个空格输出格式有严格要求的;
3、如果自己写的快排不行,那可以试试系统的快排qsort(),我的程序就是用系统的快排;
4、用G++超时,但用C++AC了,-_-!!;
5、如果你是用字符数组才存储每个字符串(电话号码),字符串包括'-'字符,建议将数组长度开到40甚至100以上;
6、为了AC可以适当通过空间换取时间;
7、看到第7点建议,说明,我也不知道为什么了,你可以把原来的程序放一边,重新考虑其他算法,比如堆排序,hash算法,等通过了,再回头看看原来   的算法有什么不妥的地方,还是不行,请看第8点建议;

8、

注:本博客与博客园上的博客为同一博客主:http://www.cnblogs.com/bestDavid/

阅读更多
个人分类: C/C++
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭