poj 1415|hdu 1298 T9 (字符串_字典树)

题目链接:http://poj.org/problem?id=1451 

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


题目大意:本题很有实际意义,模拟现实中的手机输入法,我最喜欢这种题目了,让我们在巩固算法的同时加强对现实事物的认知。言归正传,本题给定n个字符串和每个串出现的频率,每个字符串都由小写字母构成。再给定m个按键操作串,由数字1-9构成,1表示结束,其他每个按键都对应3-4个字母,和手机键盘一样。一个按键操作对应一个输出,输出的是频率最高的前缀,频率相同时输出字典序小的那个前缀。如果你还不是很明白,掏出你的手机按按看。


解题思路:这题卡了一天。第一天按照键盘的几个键来模拟,所以字典树中的next指针数组大小为10,表示0-9这几个儿子,这样查询的时候非常方便,只要进行线性转移就可以。比如说34,先从按键3的3个字符’d''e''f'找最大频率的那个串,到当前长度出现‘d''e''f'的串必定经过当前节点,然后到4的时候再做判断就可以。但很明显,这是错误的。因为通过记录最大频率的串的下标来输出前缀会出错,没办法正确输出频率最大的那个前缀。后来,经过CWW提醒,频率是要累加的,比如ha 3,hab 4,那么h的频率是7,a的频率也是7,b的频率则是4。这样只要随机输出到这个节点的字典序最小的字符串前缀就可以了。

    但错,错,错,依然是错。我一直想不通,可能是查询的时候无法正确找到要输出的前缀吧。

    所以,第二天果断换了思路,按照26个字母来模拟,这样建字典树的时候十分方便,套模版就可以。但是查询的时候就要搜索,因为按键操作是以数字的形式给出的,每个键对应3-4个字符,这样转移的时候就不止一种方法,必须每种都去尝试,然后输出频率最大并且字典序最小的前缀。我在膜拜了众人的解题报告之后,用优先队列

解决了这题。

    不过,现在代码就比较难懂了。昨天的代码写得挺飘逸的。  


测试数据:

5
3
ac 4
ab 3
bp 5
1
271


7
gewel 5
hello 3
hell 2
rubmarine 4
ruper 4
suring 3
suesday 5
6
439351
435561
7826274631
787371
7874641
78373291


3
abyb 3
abye 13
aaza 4
1
22931


5
hell 8
hello 4
idea 8
next 8
super 3
2
435561
43321


7
another 5
contest 6
follow 3
give 13
integer 6
new 14
program 4
6
1
77647261
6391
4681
26684371
77771


代码:

#include <stdio.h>
#include <queue>
#include <stdlib.h>
#include <string.h>
using namespace std;
#define MIN 120
#define MAX 1200


struct trienode {

	int sum;				 
	trienode *next[26];			
}*root;
struct prionode{

	int sum,slen;
	char str[MIN];
	trienode *trie;
	friend bool operator < (prionode a,prionode b) {	
								//这样写可以避免用两个队列,长度短的先出队
		return a.slen > b.slen || (a.slen == b.slen &&	//就可以根据长度判断该不该继续出队,不理解看Search()
			(a.sum < b.sum || (a.sum == b.sum && strcmp(a.str,b.str) > 0)));
	}
};


int n,m,p,num[MIN],keylow[MIN];							//每个按键有num[i]+1个字母,每个按键的第一个字母是keylow[i]+'a'		 
char dir[MAX][MIN],list[MIN];							//list表示按键操作串
priority_queue<prionode> qu;


void initial() {

	for (int i = 2; i <= 9; ++i)
		num[i] = 2;
	num[7] = num[9] = 3;


	keylow[2] = 0,keylow[3] = 3;
	keylow[4] = 6,keylow[5] = 9;
	keylow[6] = 12,keylow[7] = 15;
	keylow[8] = 19,keylow[9] = 22;
}
trienode *CreateNode() {

	trienode *p;
	p = (trienode *) malloc (sizeof(trienode));
	p->sum = 0;
	for (int i = 0; i < 26; ++i)
		p->next[i] = NULL;
	return p;
}
void Release(trienode *p) {

	for (int i = 0; i < 26; ++i)
		if (p->next[i] != NULL) Release(p->next[i]);
	free(p);
}


void Insert(char *dir,int pro) {

	int i = 0,k,in;
	trienode *p = root;


	while (dir[i]) {

		k = dir[i++] - 'a';
		if (p->next[k] == NULL) 
			p->next[k] = CreateNode();
		p->next[k]->sum += pro;
		p = p->next[k];
	}
}
void Search(char *list) {
	
	int i = 0,k,in,j;
	prionode now;
	list[strlen(list)-1] = '\0';	//把最后的1去了
	while (!qu.empty()) qu.pop();	//清空优先队列
	now.slen = 0,now.str[0] = '\0';
	now.trie = root,qu.push(now);
	
	while (list[i]) {
		
		in = list[i++] - '0';
		while (!qu.empty()) {
			//层次层次遍历
			now = qu.top();
			if (qu.top().slen == i) break;
			else qu.pop();
			int tp = keylow[in];


			for (j = tp; j <= num[in] + tp; ++j)
				if (now.trie->next[j] != NULL) {
					
					prionode next;
					next.trie = now.trie->next[j];
					next.sum = next.trie->sum;
					next.slen = i;
					strcpy(next.str,now.str);
					next.str[i-1] = j + 'a';
					next.str[i] = '\0';
					qu.push(next);
				}
		}
		if (qu.empty()) printf("MANUALLY\n");
		else printf("%s\n",qu.top().str);
	}	
	printf("\n");
}


int main()
{
	int i,j,k,t,cas;
	initial();


	scanf("%d",&t);
	for (cas = 1; cas <= t; ++cas) {

		root = CreateNode();
		scanf("%d",&n);
		for (i = 0; i < n; ++i)
			scanf("%s%d",dir[i],&p),Insert(dir[i],p);


		scanf("%d",&m);
		printf("Scenario #%d:\n",cas);
		for (i = 0; i < m; ++i)
			scanf("%s",list),Search(list);
		Release(root);
		printf("\n");
	}
}

本文ZeroClock原创,但可以转载,因为我们是兄弟。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值