题目背景:为了方便手机用户发短信,希望在用户按键时,根据每个词出现的频数,给出每个阶段最有可能要打的词。
PS:相同的前缀频数要相加计算。
题目大意:有不同的几组测试样例,每个测试样例中有w个词及每个词出现的频数,然后是p组要打的词,按1键表示结束输入。要求输出按到每个键时最可能要打的词。
解题思路:
根据每个单词建立一棵“单词字典树”(A树), 附加域记录每个字母出现的频数。再根据“单词字典树”建立一棵“数字字典树”(B树),附加域里记录最大频数及对应的单词下标。
下面我们细细地研究下:首先说,按每个词去建字典树应该都会。现在呢,要在建A树的同时建起B树来。所以我们需要一个映射,一个从字母到数字键盘的映射,根据这个映射关系去更新B树。比如,在“hell”和“hello”之后出现了“idea”,此时,在A树中只是新加进来个词而已,而在B树中则不然,因为idea对应的数字键为4332,和前面的hell的前两位43是相同的,因此,在B树中更新时,4和4之后3的频数值将从hell和hello的7改为idea的8,下标值也将从hello的下标2改为idea的下标3。如图:
这样,当按键时,就可以直接从B树中读取出下标来进行输出了。
OK,具体代码如下(用时47MS):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define maxn 0x0400
typedef struct word {
int p; // probability value
struct word* next[26];
}word, *W;
typedef struct dig {
int p; // probability value
int w; // word's sub in dict[]
struct dig* next[10];
}dig, *D;
// From alpha to digit
int atod[26] = {2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5,
5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9};
char dict[maxn][0x80];
W wroot = NULL;
D droot = NULL;
inline W getW() {
int i;
W p = (W)malloc(sizeof(word));
p->p = 0;
for (i = 0; i < 26; i++) {
p->next[i] = NULL;
}
return p;
}
inline D getD() {
int i;
D p = (D)malloc(sizeof(dig));
p->p = 0;
for (i = 0; i < 10; i++) {
p->next[i] = NULL;
}
return p;
}
void delw(W p) {
int i;
for (i = 0; i < 26; i++) {
if (p->next[i]) {
delw(p->next[i]);
}
}
free(p);
}
void deld(D p) {
int i;
for (i = 0; i < 10; i++) {
if (p->next[i]) {
deld(p->next[i]);
}
}
free(p);
}
int main() {
int cs, x, n, i, p, j, q;
char s[0x80];
scanf("%d", &cs);
for (x = 1; x <= cs; x++) {
W wp = wroot = getW();
D dp = droot = getD();
scanf("%d", &n);
for (i = 1; i <= n; i++) {
scanf("%s %d", s, &p);
strcpy(dict[i], s); // 存下所有的词
wp = wroot;
dp = droot;
for (j = 0; s[j]; j++) {
int pos = s[j] - 97;
if (wp->next[pos] == NULL) {
wp->next[pos] = getW();
}
if (dp->next[atod[pos]] == NULL) {
dp->next[atod[pos]] = getD();
}
wp = wp->next[pos];
wp->p += p;
dp = dp->next[atod[pos]];
// 下面是根据A树建B树的过程
if (wp->p > dp->p) {
dp->p = wp->p; // 更新probability值
dp->w = i; // 更新对应单词的下标
}
}
}
scanf("%d", &q);
printf("Scenario #%d:\n", x);
while (q--) {
scanf("%s", s);
dp = droot;
for (i = 0; s[i] != 49; i++) {
int pos = s[i] - 48;
if (dp) { // 如果有这种按键方式的词
dp = dp->next[pos];
if (dp) {
// 可能按到上一个键时还有
// 到这个键时就没有了
for (j = 0; j <= i; j++) {
putchar(dict[dp->w][j]);
}
} else {
printf("MANUALLY");
}
} else {
printf("MANUALLY");
}
printf("\n");
}
printf("\n");
}
printf("\n");
delw(wroot);
deld(droot);
}
return 0;
}
在此呢,可以单独写个空间申请以加快速度(连free也省了,用时16MS):
word WS[100000];
dig DS[100000];
inline void init() {
WTOP = DTOP = 0;
}
inline W getW() {
int i;
W p = &WS[WTOP++];
p->p = 0;
for (i = 0; i < 26; i++) {
p->next[i] = NULL;
}
return p;
}
inline D getD() {
int i;
D p = &DS[DTOP++];
p->p = 0;
for (i = 0; i < 10; i++) {
p->next[i] = NULL;
}
return p;
}
正所谓,要哈希呢,就彻头彻尾的哈希吧(用时0MS):
#include <stdio.h>
char *as = "abcdefghijklmnopqrstuvwxyz", ai[128];
char *ah = "22233344455566677778889999", di[128];
char ls[1000][101];
int w, d, l;
int wb[99927][26], wp[99927];
int db[33309][8], dp[33309], dl[33309];
void ins(char* s, int p) {
int i, j, wt, dt;
for (i = wt = dt = 0; s[i]; i++) {
if (!wb[wt][ai[s[i]]]) {
wb[wt][ai[s[i]]] = ++w;
wp[w] = 0;
for (j = 0; j < 26; j++)
wb[w][j] = 0;
}
if (!db[dt][di[s[i]]]) {
db[dt][di[s[i]]] = ++d;
dp[d] = 0;
for (j = 0; j < 8; j++)
db[d][j] = 0;
}
wt = wb[wt][ai[s[i]]];
dt = db[dt][di[s[i]]];
wp[wt] += p;
if (wp[wt] > dp[dt]) {
dp[dt] = wp[wt];
dl[dt] = l;
}
}
sprintf(ls[l++], s);
}
void tap(char* s) {
int i, j, dt;
for (i = dt = 0; s[i] > '1'; i++) {
if (!db[dt][di[s[i]]])
break;
dt = db[dt][di[s[i]]];
for (j = 0; j <= i; j++)
putchar(ls[dl[dt]][j]);
puts("");
}
for (; s[i] > '1'; i++)
puts("MANUALLY");
}
int main() {
int c, n, p, i;
char s[128];
for (p = 0; as[p]; p++) {
ai[as[p]] = p;
di[as[p]] = ah[p] - '2';
di[ah[p]] = ah[p] - '2';
}
scanf("%d", &c);
for (i = 1; i <= c; i++) {
w = d = l = 0;
for (p = 0; p < 8; p++) db[0][p] = wb[0][p] = 0;
for (; p < 26; p++) wb[0][p] = 0;
printf("Scenario #%d:\n", i);
scanf("%d", &n);
while (n--) {
scanf(" %s %d", s, &p);
ins(s, p);
}
scanf("%d", &n);
while (n--) {
scanf(" %s", s);
tap(s);
puts("");
}
puts("");
}
return 0;
}