[USACO 2014 Feb Silver]Auto-complete

14 篇文章 0 订阅
0 篇文章 0 订阅

[题目描述]

Bessie the cow has a new cell phone and enjoys sending text messages,although she keeps making spelling errors since she has trouble typing onsuch a small screen with her large hooves. Farmer John has agreed to helpher by writing an auto-completion app that takes a partial word and suggests how to complete it.

The auto-completion app has access to a dictionary of W words, each consisting of lowercase letters in the range a..z, where the total number of letters among all words is at most 1,000,000. The app is given as input a list of N partial words (1 <= N <= 1000), each containing at most 1000 lowercase letters. Along with each partial word i, an integer K_i is also provided, such that the app must find the (K_i)th word in alphabetical order that has partial word i as a prefix. That is, if one ordered all of the valid completions of the ith partial word, the app should output the completion that is (K_i)th in this sequence.


为什么我要放英文的题面呢。。因为中文题面翻译的不好。。很多条件没加进去。。

这道题就是给定一个字符串,求整个字典中以这个为前缀的所有字符串中第k大。。

既然是前缀,看到的第一眼就想到了trie..

最开始的想法是先把他们读进来,然后排个序,离散化一下,然后建trie树

读入一个前缀后,把这个前缀在trie上跑过之后,跑完之后会停在一个节点上。。

然后就找这个节点的所有有end标记儿子的对应的离散化之后的值找第k小,然后输出相应的编号。。

想了想觉得空间会爆炸。。

然后想到一个新的方法。。

边读边建trie树,在建树的时候顺便记录一下经过这个节点的字符串数

读入一个前缀后,把这个前缀在trie上跑过之后,跑完之后会停在一个节点上。。

接下来我要找第k个小
那么先找它下面的a
如果经过a的小于k,那么接着找b,不断递归下去。。
Code:
const shuru='auto.in';
	  shuchu='auto.out';
	  maxlen=1000;
type  point=^node;
	  node=record
			endless,data:longint;
			next:array['a'..'z'] of point;
		   end;
	  str=array[0..maxlen] of char;
var	start,p,q:point;
	s:str;
	len,i,j,k,n,m:longint;
	ch:char;
procedure neww(var p:point);
begin
	new(p);
	p^.data:=0;
	p^.endless:=0;
	for ch:='a' to 'z' do p^.next[ch]:=nil;
end;
procedure forever(s:str;len,sign:longint);
var i:longint;
begin
	p:=start;
	for i:=1 to len do
	begin
		if p^.next[s[i]]=nil then begin
									neww(p^.next[s[i]]);
									p:=p^.next[s[i]];
								  end
							 else p:=p^.next[s[i]];
		inc(p^.data);
	end;
	p^.endless:=sign;
end;
function ever(s:str;len,k:longint):longint;
var i:longint;
begin
	p:=start;
	for i:=1 to len do
	begin
		if p^.next[s[i]]=nil then exit(-1);
		p:=p^.next[s[i]];
		if p^.data<k then exit(-1);
	end;
	while not((k=1) and (p^.endless<>0)) do
	begin
		if p^.endless<>0 then dec(k);
		for ch:='a' to 'z' do
			if p^.next[ch]<>nil then if p^.next[ch]^.data>=k then begin
																p:=p^.next[ch];
																break;
														   end
												   else k:=k-p^.next[ch]^.data;
	end;
	exit(p^.endless);
end;
procedure init;
begin
	neww(start);
	readln(n,m);
	for i:=1 to n do
	begin
		len:=0;
		while not(eoln) do
		begin
			inc(len);
			read(s[len]);
		end;
		readln;
		forever(s,len,i);
	end;
end;
procedure main;
begin
	init;
	for i:=1 to m do
	begin
		len:=0;
		read(k);
		read(s[1]);
		while not(eoln) do
		begin
			inc(len);
			read(s[len]);
		end;
		writeln(ever(s,len,k));
	end;
end;
begin
	main;
end.
		

extra:
lzw大神还想出了一个非常巧妙的方法。。这里我说一下。。
先把所有的字符串读进来,排序
在读进一个前缀后,将它用插入排序的方法插进去。。
假设插进去之后的下标是x
那么判断a[x+k],如果是它是它的前缀,就writeln(a[x+k]),否则writeln(-1);
为什么呢。。
因为我们知道前缀相同的在排序之后肯定是在一起的。。
所以如果存在一个第k小的话,它一定是在a[x+k]上的。。
真是妙哉妙哉。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值