poj 1002 487-3279 map的使用

487-3279
Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 232988 Accepted: 40620

Description
企业喜欢用容易被记住的电话号码。让电话号码容易被记住的一个办法是将它写成一个容易记住的单词或者短语。例如,你需要给滑铁卢大学打电话时,可以拨打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

====================================================================================

题目大意:中文题我就不解释了。


解题思路:最朴素的想法是每接进来一个电话号码,就整理成正确格式,如果已经存储过同一个电话号码,则增加该号码的数量,如果没存过则存起来,数量置为1。

用数组或者容器来实现朴素思路的时间复杂度是O(N^2),其中N达到10^5,10^5*10^5=10^10,2s/2.5GHz的数量级才10^9,显然有很大超时的危险。

如果仍然使用朴素的思路则应该在存电话号码的数据结构上动脑筋。有键(电话号码),有值(出现次数),还需要排序,可以使用STL中的map容器,map内部维护着一颗红黑树,原理这里不细讲了,查找效率O(logN),这样一来总的时间复杂度是O(NlogN)<17*10^5;

还可以改进思路,10^5个电话号码,1个电话号码7个字节占不到700k,可以接受。实际上如果用一个int存一个7位数则只需要400k了。我们可以全接进来只排一次序,然后遍历一次完成输出。STL的sort时间复杂度是O(NlogN),具体点是N*以2为底N的对数,N=10^5时复杂度小于17*10^5。

虽然两种办法时间复杂度是一个数量级的,但是map内部的维护的复杂的,会明显占用更多内存,下面可以比较一下。


map用法:

头文件:#include<map>即可

定义:map<keyType,valueType> m;

            其中keyType是主键的数据类型,valueType是值的数据类型,主键和自己的值形成映射。map默认按主键大小升序排列,如果主键是自定义数据类型需要重载小于号。

插入数据:有四种方式

         (1) m["a"] = 1;                          //这种方式会覆盖原键值对的值
         (2) m.insert(map<string, int>::value_type("b",2));
         (3) m.insert(pair<string,int>("c",3));
         (4) m.insert(make_pair<string,int>("d",4));

查找数据:

        (1)int a = m["a"];

        (2)map<string,int>::iterator it;

             it = m.find("a");

             int a = it->second;               //对于map的迭代器,it->first是主键,it->second是值

修改数据:

        (1)m["a"] = a;

        (2)it->second = a;                    //注意,主键一经插入,不可修改,只有值可修改

删除数据:

        (1)m.erase("a");

        (2)m.erase(it);                          //可以用主键或迭代器删除键值对

其他方法:如begin(),end(),size(),empty(),clear(),swap()等方法跟其他STL容器的一个用法。

用map:

//Memory: 3832K	Time: 750MS

#include<cstdio>
#include<map>
using namespace std;
int leter[26] = {2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,0,7,7,8,8,8,9,9,9,0};//26个字母分配值
map<int,int> m;

int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        char s0[100];                                                 //题目并没有说一个字符串有多长,开100足够
        int s2 = 0;
        scanf("%s",s0);
        for( int i = 0 ; s0[i] != '\0' ; i++ )
        {
            if( s0[i] == 'Q' || s0[i] == 'Z' || s0[i] == '-' )        //跳过没有对应值的字符
                continue;
            if( s0[i] >= '0' && s0[i] <= '9' )
                s2 = s2*10+s0[i]-'0';                                 //将电话号码当做7位整数逐位生成
            else if( s0[i] >= 'A' && s0[i] <= 'Z' )
                s2 = s2*10+leter[s0[i]-'A'];
        }
        map<int,int>::iterator it;
        it = m.find(s2);
        if( it == m.end() )                                           //如果map中没有这个电话号码则插入键值对
            m[s2] = 1;                                                //出现次数初始化为1
        else
            it->second++;                                             //如果电话号码已在map中,出现次数加1
    }
    map<int,int>::iterator it;
    bool exist = false;
    for( it = m.begin() ; it != m.end() ; it++ )
        if( it->second > 1 )                                          //按格式输出出现次数大于1的电话号码
        {
            printf("%03d-%04d %d\n", it->first/10000 , it->first%10000 , it->second);
            exist = true;
        }
    if(!exist)                                                        //如果没有重复出现的电话号码,输出特定字符串
        printf("No duplicates.\n");
    return 0;
}
用sort:
//Memory: 940K	Time: 469MS

#include<cstdio>
#include<algorithm>
using namespace std;
int leter[26] = {2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,0,7,7,8,8,8,9,9,9,0};

int main()
{
    int n;
    scanf("%d",&n);
    int *l = new int[n];
    for( int j = 0 ; j < n ; j++ )
    {
        char s0[50];
        int s2 = 0;
        scanf("%s",s0);
        for( int i = 0 ; s0[i] != '\0' ; i++ )
        {
            if( s0[i] == 'Q' || s0[i] == 'Z' || s0[i] == '-' )
                continue;
            if( s0[i] >= '0' && s0[i] <= '9' )
                s2 = s2*10+s0[i]-'0';
            else if( s0[i] >= 'A' && s0[i] <= 'Z' )
                s2 = s2*10+leter[s0[i]-'A'];
        }
        l[j] = s2;                                     //每生成一个7位数存入数组中对应位置
    }
    sort(l,l+n);                                       //升序排序,不用重载小于号
    bool exist = false;
    int cnt = 1;
    for( int i = 1 ; i < n ; i++ )                     //每个重复电话号码输出出现的最后一个,和出现的次数
    {
        if( l[i] == l[i-1] )
        {
            cnt++;
            exist = true;
        }
        if( l[i] > l[i-1] || i == n-1 )
        {
            if(cnt > 1)
                printf("%03d-%04d %d\n", l[i-1]/10000,l[i-1]%10000,cnt);
            cnt = 1;
        }
    }
    if(!exist)
        printf("No duplicates.\n");
    return 0;
}
这道题字符串数据量大,考验输入输出效率,我用cin、cout始终没能过。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值