UVA 12526 Cellphone Typing (字典树)
tags: acm
模板题,虽然之前没去学,但是还是在比赛的时候现学现卖搞定了,算是我写的第一道字典树吧.
题意:
给你一个字典,包含许多单词.每当你按一个键时,系统会将其补全为所有以当前内容为前缀的单词的公共前缀.
如第一个样例,字典中的词为”hell”,”hello”,”heaven”,”goodbye”四个单词.
当你输入’h’时,系统将为你补全为”hell”,”hello”及”heaven”的公共前缀”he”,再输入’l’,继续补全为”hell”从而完成了”hell”的输入,你按了两次键(‘h’和’l’).
求输入字典中的单词所需要的次数的期望.
解析:
因为每次输入都会使得当前输入接近目标单词,因此这道题相当于统计字典中的单词在字典树中经过的的分叉数(每个分叉需要通过输入来确定到底是哪个单词),求和后除以单词书就能得到所求的期望,需要注意的是单词结尾也算是一个分叉.
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <queue>
#define MAXN 1000000+10000
using namespace std;
const int sigma_size = 27;
const int maxnode = 1000000 + 10000;
struct Trie
{
int ch[maxnode][sigma_size];
int val[maxnode];
int sz;
Trie() { sz = 1; memset(ch[0], 0, sizeof(ch[0]));memset(val, 0, sizeof(val));}
int idx(char c) { return c - 'a'; }
void insert(char * s, int v)
{
int u = 0, n = strlen(s);
for (int i = 0; i < n; i++)
{
int c = idx(s[i]);
if (!ch[u][c])
{
memset(ch[sz],0,sizeof (ch[sz]));
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u] = v;
}
int query(char *s)
{
int ret = 0;
int len = strlen(s);
int pos = 0;
for (int i = 0; i < len; i++)
{
int cnt = 0;
for (int j = 0; j < sigma_size; j++)
{
if (ch[pos][j] != 0)
cnt++;
}
if (val[pos])
cnt++;
if (cnt > 1||i==0)
{
ret++;
}
pos = ch[pos][idx(s[i])];
}
return ret;
}
void init()
{
sz = 1; memset(ch[0], 0, sizeof(ch[0])); memset(val, 0, sizeof(val));
}
}dict;
char str[MAXN][100];
int main()
{
int N;
while (~scanf("%d", &N))
{
dict.init();
for (int i = 0; i < N;i++)
{
scanf("%s",str[i]);
dict.insert(str[i], 1);
}
int SUM = 0;
for (int i = 0; i < N; i++)
{
int cnt = dict.query(str[i]);
SUM += cnt;
}
printf("%.2lf\n", (SUM*1.0) / N);
}
return 0;
}