HDU: http://acm.hdu.edu.cn/showproblem.php?pid=1298php
POJ: http://poj.org/problem?id=1451ios
題目大意:手機上有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,表示後面接有一個單詞。spa
輸出:對應每個數字串,每按一個數字就輸出相應的字符串,不存在則輸出」MANUALLY「,不然輸出頻率最大的字符串,若是頻率相同則按字典序輸出最前的那一個。處理完一個數字串後,在後面再輸出一行空行,每個用例後也要有一個空行。.net
分析: 看完長長的一段題目,發現這是一道典型的字典樹(tire tree)題,每按一個鍵,咱們就要找出該數字鍵對應的字母所在的串,直處處理完這個數字序列。這個不難實現,基本上就是字典樹的基本功能。指針
但咱們從中分析出約束條件 一、輸出對應的字符串 二、要求頻率是最多的 三、字典序輸出 四、格式控制(每個數字串處理後要再輸出額外的空行,每個用例結束後也要有一個空行)。blog
要實現這些約束條件,按經驗法則,先優先處理約束最強的那一個條件,字典序,爲什麼是字典序是最難的呢?(其實也是最簡單的)由於咱們在從根往下找時,若是 碰到相同的頻率的詞彙,就要比較哪個字典序較靠前,由於樹是非線性的,要進行比較實現比現困難,但咱們發現,若是咱們先碰到一個頻率最大的,此時它的字 典序也是最靠前的,由於按鍵上的字母也是按字典序來進行排序的,咱們的查找也是按字典序來進行查找的,這樣就避免了比較,直接查找頻率最大的就好了。如圖 所示:排序
在輸入」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, °ree );
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