HDU-1298 | POJ-1451-T9

HDU: http://acm.hdu.edu.cn/showproblem.php?pid=1298

POJ:  http://poj.org/problem?id=1451

题目大意:手机上有0~9共十个数字按键,其中只有2到9有对应的英文字母,你要打一 个单词,比如hello,那就要按键  4 共 两 次, 键 3 共 两 次, 键5 共 三 次,再按5键共三次,再按键6三次,为了减少按键数,开发商发明了一种新的规则叫 “ T9 ”,这个规则是根据单词的出现频率来实现单词的按键组合功能,比如,“hello" , "he",  "hell" 的出现频率分别为 3, 4, 5, 那么字母 ‘h’ 出现的频率为 3 + 4 + 5 =12,‘o'出现的频率为1. 那么在按 "hello" 的话,只需要按4,3,5,5,6,共五次就可以了,大大减少了按键次数,现要求你模拟这个规则,输出相应的单词,如果该单词不存在,则输出 “MANUALLY”。

输入: 第一行  有一个整数表示  测试用例数

            每一个用例 先有一个整数N(0~1000,包括0和1000),表示单词数, 接下来N行,每行有一个单词(长度最长为100)和一个频率数P(1—100),接着有一个整数M,表示要输入的按键数字。结尾为1,表示后面接有一个单词。

输出:对应每一个数字串,每按一个数字就输出相应的字符串,不存在则输出”MANUALLY“,否则输出频率最大的字符串,如果频率相同则按字典序输出最前的那一个。处理完一个数字串后,在后面再输出一行空行,每一个用例后也要有一个空行。

      分析: 看完长长的一段题目,发现这是一道典型的字典树(tire tree)题,每按一个键,我们就要找出该数字键对应的字母所在的串,直到处理完这个数字序列。这个不难实现,基本上就是字典树的基本功能。

      但我们从中分析出约束条件  1、输出对应的字符串  2、要求频率是最多的 3、字典序输出  4、格式控制(每一个数字串处理后要再输出额外的空行,每一个用例结束后也要有一个空行)。

      要实现这些约束条件,按经验法则,先优先处理约束最强的那一个条件,字典序,为何是字典序是最难的呢?(其实也是最简单的)因为我们在从根往下找时,如果 碰到相同的频率的词汇,就要比较哪一个字典序较靠前,因为树是非线性的,要进行比较实现比现困难,但我们发现,如果我们先碰到一个频率最大的,此时它的字 典序也是最靠前的,因为按键上的字母也是按字典序来进行排序的,我们的查找也是按字典序来进行查找的,这样就避免了比较,直接查找频率最大的就行了。如图 所示:

        

     在输入”221”时,先处理第一个2,输出“c”,再处理第二个2时,输出"bc",不用再考虑“ca”了。另外,在要输出某个串时,我增加了一个额外的父指针,那么在某个结点要输出这个词汇时,回溯就可以了。

#include<iostream>

#include<cstdio>

#include<cstring>

#include<queue>

#define MAX26

usingnamespace std;

int key_str[10][4]={ {-1}, {-1},{0,1,2}, {3,4,5},

                            {6,7,8}, {9,10,11}, {12,13,14},

                            {15,16,17,18}, {19,20,21}, {22,23,24,25}

                   };

int len_str[10]={ 1, 1, 3, 3, 3, 3, 3, 4, 3, 4 };

struct tire

{

   int degree,data;

   struct tire *next[MAX], *father;

    tire()       //此构造函数只有用C++的new操作符才有效,malloc无效,否则使用malloc出错

   {

       for( int i=0; i<MAX; i++ )next[i]= NULL; degree=data=0,father=NULL;

   }

};

int insert(char *str, struct tire *root, int de )

{

   int len = strlen(str ), pos;

   struct tire *read =root;

   for( int i=0; i<len; i++ )

   {

        pos = (int)( str[i] - 'a' );

       if( !read->next[pos]  )

                   read->next[pos] = new struct tire;

        read->next[pos]->father = read;  //记录父结点

        read =read ->next[pos];   //移到下一个位置

        read->data = pos;        

        read->degree+=de;         //所经过的字母的频率均要累加

   }

   return 0;

}

int PRINTF(struct tire *root )   //打印词汇

{

   if( !root ) return 0;

   int str[110],len=0,i;

   while( root->father )     //从结点向根结点走

   {

        str[len++]=root->data;   

        root=root->father;

   }

   for( i=len-1; i>=0; i-- ) printf( "%c",'a'+str[i] );

    printf( "\n" );

}

bool find( intx, struct tire *root )

{

   if( root == NULL || root->next[x] ==NULL ) return false;

   else return true;

}

int work( char *str, struct tire *root )

{

   int len = strlen(str ), pos,store_num, i, j,k, record_store;

   struct tire *read=NULL, *maxpriority=NULL;

    queue<struct tire*>store;

    store.push( root );

   for( i=0,store_num =1; i<len-1; i++ ) //最外层表示按键顺序

   {

        maxpriority=NULL;

        pos = (int)( str[i] - '0' );   //按键数字

       for( j=0,record_store=0; j<store_num; j++ )   //查找到可能的单词数

       {

            read= store.front();    //取出起始查找位置

            store.pop();            //退出队列

           for( k=0; k<len_str[pos]; k++ )   //每一个按键对应的多个字母

           {

               if( find( key_str[pos][k] ,read ) )   //key_str表示按键pos对应多个字母的其中第k个字母

               {

                   if( !maxpriority )

                              maxpriority= read->next[ key_str[pos][k] ];

                   else if( maxpriority->degree <

                                        read->next[key_str[pos][k] ] ->degree )

                        {    //这里有点复杂,先是找到这个按键pos对应的第k个字母所代表的整数值,见代码初始段,如果是字母'b',那么值就为'b'-'a',然后在这个结点找它的儿子指针的频率

                             maxpriority = read->next[ key_str[pos][k] ];

                        }

                    store.push(read->next[ key_str[pos][k] ] );

                    record_store++;

               }

           }

       }

       if( record_store == 0 ) {store_num=0; printf( "MANUALLY\n" ); }

       else

       {

            store_num = record_store;

            PRINTF(maxpriority );

            maxpriority=NULL;

       }

   }

}

int dealtire(struct tire *root )

{

   for( int i=0; i<MAX; i++ )

   {

       if( root->next[i] )dealtire( root->next[i] );

   }

   delete root;

}

int main()

{

   int t, word_num,press_num, i, j,degree, s=0;

   char word[110];

    scanf( "%d",&t );

   while( t-- )

   {

       struct tire *root = new struct tire;

        scanf( "%d",&word_num );

       for( i=0; i<word_num;i++ )

       {

            scanf( "%s%d",word, &degree );

            insert(word, root,degree );

       }

        printf( "Scenario #%d:\n", ++s );

        scanf( "%d",&press_num );

       for( i=0; i<press_num;i++ )

       {

            scanf( "%s",word );

            work(word, root);

            printf( "\n" );

       }

        printf("\n");

        dealtire(root );

   }

}



http://blog.csdn.net/pandeng4639088/article/details/7856383

转载于:https://my.oschina.net/u/574940/blog/343372

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值