您的位置:首页 > 其它

AOJ-AHU-OJ-673 聪明的输入法

2014-04-09 21:34 351 查看
聪明的输入法

Time Limit: 10000 ms   Case Time Limit: 2000 ms   Memory Limit: 128 MB

Description

众所周知,XX输入法具备强大的学习功能,对于用户来说,感觉输入法像一个会学习的机器人,比如,当用户第一次输入“sxl”,输入法第一个提示“少写了”,

如果用户选择“睡醒了”,当第二次用户再输入“sxl”的时候,“睡醒了”这个词就会出现在比较靠前的位置,神奇吧,输入法就像一个小孩子,在你的指导下慢慢学会每个拼音的含义。

现在让我们一起来实现下这个神奇的功能吧,为方便起见,我们假设用户是美国籍的(仅输入小写英语字母),每当该用户输入一个单词时,请输出和该用户输入习惯最为匹配的单词(以该单词为前缀的输入频率最高的单词),如果有多个则输出字典序最小的,如果没有的话,则直接输出该单词。

Input

单组测试数据

第一行输入一个整数N(1<= n <= 105),表示用户输入单词的个数。

接下来N行由小写字母组成的英文单词,每个单词的长度大于0且不超过10。

Output

请输出n个最为匹配的单词,

第i行输出对应第i个输入单词。

Sample Input
OriginalTransformed
5
abc
abd
abc
ab
a


Sample Output
OriginalTransformed
abc
abd
abc
abc
abc


Source

“讯飞输入法杯”安徽大学第六届程序设计竞赛

[align=center]————————————————————忧桑的分割线————————————————————[/align]
[align=center]思路:字典树的应用……难度还是有的。但是学姐的解题报告上说该题是模板题,题解省略,QAQ。思路是酱紫的:在字典树的数据域储存三样东西:1.单词出现次数 2.以该单词为前缀的单词的最大频率 3.以该单词为前缀的最大频率的单词。[/align]
[align=center]每完成一次输入(插入),都需要做以下两件事:1.输出答案 2.逆向维护这个字典[/align]
[align=center]对字典树的逆向维护是比较困难的……假如你不熟悉递归的话。因为只能从根结点开始向下找儿子,而维护的时候,一路上的父亲都要维护。那么DFS是非常必要的。将Insert()函数改写成递归即可。模板Insert的思路是一个for循环插入一个单词,现在改成DFS整个单词,一个字母一个字母Insert,在递归的边界上输出之前维护好的答案。输出之后在return每一层DFS的过程中,维护这个字典树。[/align]
[align=center]代码如下:[/align]
#include <cstring>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <string>
#include <iostream>
using namespace std;
const int MAXN = 1000010;
int nxt;
char dic[15];
struct Trienode {
Trienode* next[26];
int cnt, maxi;
char str[15];
}trie[MAXN];

Trienode* newnode() {
memset(&trie[nxt], 0, sizeof(Trienode));
return &trie[nxt++];
}

int Insert(Trienode* rt, char* s) {
if(s[0] == '\0') {
//维护好的字典树上储存的都是最大频率单词,故不会出现该单词之前出现的频率大于最大频率
if(rt->cnt == rt->maxi)//就两种情况:1.之前出现该单词的频率和最大一样;2.当前单词没出现过(cnt = maxi = 0)
printf("%s\n", dic);//由于按字典序输出,所以频率一样的时候,该单词的字典序一定是最小的(它本身就是最短前缀)
else
printf("%s\n", rt->str);
rt->cnt++;//题干要求在输入时只考虑之前的出现频率
return rt->cnt;
}
else {
int c = s[0] - 'a';
if(!rt->next[c]) rt->next[c] = newnode();
rt = rt->next[c];
int ret = Insert(rt, s+1);//递归插入单词,一个字母一个字母来。边界条件是该单词插完
if(ret > rt->maxi) {
strcpy(rt->str, dic);
rt->maxi = ret;
}
else if(ret == rt->maxi&&strcmp(rt->str, dic) > 0)
strcpy(rt->str, dic);
//以上是两种情况的维护:最大频率改变;字典序
return ret;//逆向维护字典树,每一层DFS返回一个ret,表示频率
}
}

int main() {
nxt = 0;
Trienode* root = newnode();
int cas;
scanf("%d", &cas);
while(cas--) {
scanf("%s", dic);
Insert(root, dic);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: