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, °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