UVA 12526 Cellphone Typing (字典树)

来源:互联网 发布:魅影传说坐骑进阶数据 编辑:程序博客网 时间:2024/04/30 20:08

UVA 12526 Cellphone Typing  (字典树)



Cellphone Typing

5000ms
131072KB
This problem will be judged on UVA. Original ID: 12526
64-bit integer IO format: %lld      Java class name: Main
Prev Submit Status Statistics Discuss Next
Font Size:  
Type:  

[PDF Link]

A research team is developing a new technology to save time when typing text messages in mobile devices. They are working on a new model that has a complete keyboard, so users can type any single letter by pressing the corresponding key. In this way, a user needs P keystrokes to type a word of length P.

However, this is not fast enough. The team is going to put together a dictionary of the common words that a user may type. The goal is to reduce the average number of keystrokes needed to type words that are in the dictionary. During the typing of a word, whenever the following letter is uniquely determined, the cellphone system will input it automatically, without the need for a keystroke. To be more precise, the behavior of the cellphone system will be determined by the following rules:

  1. The system never guesses the first letter of a word, so the first letter always has to be input manually by pressing the corresponding key.
  2. If a non-empty succession of letters c1c2...cn has been input, and there is a letter c such that every word in the dictionary which starts with c1c2...cn also starts with c1c2...cnc, then the system inputs c automatically, without the need of a keystroke. Otherwise, the system waits for the user.

For instance, if the dictionary is composed of the words `hello', `hell', `heaven' and `goodbye', and the user presses `h', the system will input `e' automatically, because every word which starts with `h' also starts with `he'. However, since there are words that start with `hel' and with `hea', the system now needs to wait for the user. If the user then presses `l', obtaining the partial word `hel', the system will input a second `l' automatically. When it has `hell' as input, the system cannot guess, because it is possible that the word is over, or it is also possible that the user may want to press `o' to get `hello'. In this fashion, to type the word `hello' the user needs three keystrokes, `hell' requires two, and `heaven' also requires two, because when the current input is `hea' the system can automatically input the remainder of the word by repeatedly applying the second rule. Similarly, the word `goodbye' needs just one keystroke, because after pressing the initial `g' the system will automatically fill in the entire word. In this example, the average number of keystrokes needed to type a word in the dictionary is then (3 + 2 + 2 + 1)/4 = 2.00.

Your task is, given a dictionary, to calculate the average number of keystrokes needed to type a word in the dictionary with the new cellphone system.

Input 

Each test case is described using several lines. The first line contains an integer N representing the number of words in the dictionary ( 1$ \le$N$ \le$105). Each of the next N lines contains a non-empty string of at most 80 lowercase letters from the English alphabet, representing a word in the dictionary. Within each test case all words are different, and the sum of the lengths of all words is at most 106.

Output 

For each test case output a line with a rational number representing the average number of keystrokes needed to type a word in the dictionary. The result must be output as a rational number with exactly two digits after the decimal point, rounded if necessary.

Sample Input 

4hellohellheavengoodbye3hiheh7structurestructuresrideridersstresssolsticeridiculous

Sample Output 

2.001.672.71



题意:首先是一个n,表示后面有n行字符串。由小写字母组成然后通过类似于现在的打字,要是遇到相同的字母,后面就马上连下去,比如第一个案例hello,hell,heaven,goodbye,当输入h的时候,由于h开头的字符串有三个,而且第二个都是e,所以e会直接输出,而当再输入一个a的时候,heaven就直接出来了而当输入的不是a,是l的时候,因为hello和hell从第三个开始的相同是hell所以,hell直接出来了。而当第一个输入的是g的时候,因为g的字符串只有一个,所以直接出goodbye所以hello要输入3次hell要输入2次heaven要输入2次goodbye要输入1次答案就是(3+2+2+1)/ 4题解:用字典树来做,字典树是一种哈希树的变种,用来排序,储存,统计字符串,当然不单单是字符串它是用结构体来储存的,然后通过结构体中的next[]来表示后面接着什么字符。它是用公共前缀放在一起,这样就可以节省很多的空间。组建树的时候通过将每个字符串末尾进行flag标记为-1(如hell和hello中,在hell后面的l这里,要标记-1),而且将分叉点也标记为-1(如hello和heaven,中a这里要标记-1),然后当遍历的时候,每遇到一个flag为-1的时候就将那个字符串所需输入的次数加1。要注意,每次的第一个字母,都是要输入的。后面当有分叉的时候,表明分叉的地方有分歧,输入时不会蹦出来,所以也要输入一次,如hello,heaven,但输入h的时候,只会蹦出he,后面的a要在输入当有其它字符串当作前缀的时候,入hell,hello,要输入hello的时候,只会蹦出hell,后面还要自己输入一个o也就只要关注这样三个地方就可以了,要是同时是末尾和分叉,只要加一次就够了,不要两次叠加#include <stdio.h>#include <stdlib.h>#include <string.h>#include <math.h>#include <algorithm>#include <iostream>using namespace std;#define maxn 200005typedef struct Trie{            //字典树所用到的结构体int flag;                   //用来标记这个结点是不是末尾,或者分叉点Trie *next[26];        //用了next,就可以不用一个个记录下字符了,可以用第几个来表示,要是小写字母,就是26,要是加上大写字母,就是52,这样类推}Trie;char str[maxn][100];             //存放字符串,用于构造树,和查询遍历的时候用Trie *root;                      //首个结点void Insert(char *ch){int len = strlen (ch), i, j, temp1 = 0, temp2 = 0;Trie *p = root, *q;for (i = 0; i < len; ++i){int id = ch[i] - 'a';               //表示这个字符属于第几个位置,用位置来表示字符if (p->next[id] == NULL){           //如果到这里开始,后面没有和这个字符串类似的前缀字符了,就开始分叉q = (Trie *) malloc (sizeof (Trie));        //新构建一个结点,来存放开始的分叉if (temp1 == 1 && temp2 == 0){                //这里是用来标记分叉时候的flagp->flag = -1;temp2 = 1;}q->flag = 1;for (j = 0; j < 26; ++j)                //新建结点要将next[]都赋值为NULLq->next[j] = NULL;p->next[id] = q;p = q;}else {temp1 = 1;                //这里表示相同前缀p = p->next[id];}}p->flag = -1;}int Query(char *ch){                     //用来记录每个字符串所需要输入几次int ant = 1, len = strlen (ch), i;Trie *p = root;if (len == 1)return 1;for (i = 0; i < len; ++i){int id = ch[i] - 'a';if (p->next[id]->flag == -1 && i != len - 1)   //这里还要注意,不要将自己的末尾当作标记来加1,所以要除去i ==len - 1时的情况ant++;p = p->next[id];}return ant;}int main (){//freopen ("in.txt", "r", stdin);int n, len, i;while (scanf ("%d", &n) != EOF){getchar();root = (Trie *) malloc (sizeof (Trie));    //构建根节点for (i = 0; i < 26; ++i)root->next[i] = NULL;for (i = 1; i <= n; ++i){scanf ("%s", str[i]);Insert(str[i]);                //构造字典树}int sum = 0;for (i = 1; i <= n; ++i)               //遍历,确定每个字符串要输入几次sum += Query(str[i]);printf ("%.2lf\n", sum * 1.0 / n);}return 0;}/*3aababc2.00*/


0 0