统计难题 |
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131070/65535 K (Java/Others) |
Total Submission(s): 700 Accepted Submission(s): 299 |
Problem Description
Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).
|
Input
输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.
注意:本题只有一组测试数据,处理到文件结束. |
Output
对于每个提问,给出以该字符串为前缀的单词的数量. |
Sample Input
banana
band
bee
absolute
acm
ba
b
band
abc
|
Sample Output
2
3
1
0
|
Author
Ignatius.L
|
Recommend
Ignatius.L
|
这道题,我直接套用模板,修改了参数和输入输出。
用的是链表的模板,一直MLE。从网上找别人的代码也是MLE,后来知道用C++提交就能过,用G++就会MLE。
不过用数组模板都能过,以后还是用数组模板比较好。
参考来源:http://www.cnblogs.com/yym2013/p/3780621.html
不知道为什么很多人的链表代码都没有删除节点释放内存。
如果
Memory Limit: 32768/32768 K (Java/Others)
就不能用字典树了。
注意如何判断空行:(前两种来源于链接)
1、用strlen()计算字符串的长度,如果长度为0,说明为空行,退出输入循环。
2、用gets()读入。读入的回车符会自动转换为NULL。所以循环读入,每次检测读入进来的字符串的第一个字符是否为NULL即可。
1、用strlen()计算字符串的长度,如果长度为0,说明为空行,退出输入循环。
2、用gets()读入。读入的回车符会自动转换为NULL。所以循环读入,每次检测读入进来的字符串的第一个字符是否为NULL即可。
3、string类geitline(cin,str)读入,用str.size() ,
计算字符串的长度,如果长度为0,说明为空行,退出输入循环。见代码一
#include <iostream>
#include<cstdlib>
#include<string>
#define MAX 26
using namespace std;
typedef struct TrieNode //Trie结点声明
{
int cnt;
struct TrieNode *next[MAX]; //儿子分支
}Trie;
void insert(Trie *root, const char *s) //将单词s插入到字典树中
{
if (root == NULL || *s == '\0')
return;
int i;
Trie *p = root;
while (*s != '\0')
{
if (p->next[*s - 'a'] == NULL) //如果不存在,则建立结点
{
Trie *temp = (Trie *)malloc(sizeof(Trie));
for (i = 0; i<MAX; i++)
{
temp->next[i] = NULL;
}
temp->cnt = 1;
p->next[*s - 'a'] = temp;
p = p->next[*s - 'a'];
}
else
{
p = p->next[*s - 'a'];
p->cnt++;
}
s++;
}
//单词结束的地方标记此处可以构成一个单词
}
int search(Trie *root, const char *s)
{
Trie *p = root;
while (p != NULL&&*s != '\0')
{
p = p->next[*s - 'a'];
s++;
}
if (p != NULL)
{
return p->cnt;
}
return 0;
}
void del(Trie *root) //释放整个字典树占的堆区空间
{
int i;
for (i = 0; i<MAX; i++)
{
if (root->next[i] != NULL)
{
del(root->next[i]);
}
}
free(root);
}
int main()
{
Trie *root = (Trie *)malloc(sizeof(Trie));
for (int i = 0; i<MAX; i++)
{
root->next[i] = NULL;
}
string s;
char s2[20];
while (getline(cin,s))
{
if (s.size() == 0)break;
for (int i = 0; i < s.size(); i++)
s2[i] = s[i];
s2[s.size()] = '\0';
insert(root, s2);
}
while (scanf("%s",s2)!=EOF)
{
int ans = search(root, s2);
printf("%d\n",ans);
}
del(root); //释放空间很重要
return 0;
}
数组做法
#include <iostream>
#include <stdio.h>
using namespace std;
int trie[1000010][26]; //数组形式定义字典树,值存储的是下一个字符的位置
int num[1000010]={0}; //附加值,以某一字符串为前缀的单词的数量
int pos = 1;
void Insert(char word[]) //在字典树中插入某个单词
{
int i;
int c = 0;
for(i=0;word[i];i++){
int n = word[i]-'a';
if(trie[c][n]==0) //如果对应字符还没有值
trie[c][n] = pos++;
c = trie[c][n];
num[c]++;
}
}
int Find(char word[]) //返回以某个字符串为前缀的单词的数量
{
int i;
int c = 0;
for(i=0;word[i];i++){
int n = word[i]-'a';
if(trie[c][n]==0)
return 0;
c = trie[c][n];
}
return num[c];
}
int main()
{
char word[11];
while(gets(word)){
if(word[0]==NULL) //空行。gets读入的回车符会自动转换为NULL。
break;
Insert(word);
}
while(gets(word))
printf("%d\n",Find(word));
return 0;
}