HDU-1298 | POJ-1451-T9

                                                      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 );

   }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章