结构之美——字典树

本文介绍了如何使用Trie树(字典树)解决统计不重复发送新年祝福消息的朋友数量的问题。在给定的朋友列表和已发送消息的发送者列表中,通过Trie树建立字典,找出未发送消息的朋友数量。题目来源于HDU 1251,要求处理无序、不区分大小写的字母字符串,并考虑发送者可能发送多次消息的情况。
摘要由CSDN通过智能技术生成

辉爷:Trie树的实现

v_JULY_v:从Trie树(字典树)谈到后缀树(10.28修订)

 

应用题目:http://acm.hdu.edu.cn/showproblem.php?pid=1251

建立字典树,求以……为前缀的单词个数

 统计难题

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131070/65535 K (Java/Others)
Total Submission(s): 3656    Accepted Submission(s): 1295

Problem Description
Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).
 

 

Input
输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.

注意:本题只有一组测试数据,处理到文件结束.
 

 

Output
对于每个提问,给出以该字符串为前缀的单词的数量.
 

 

Sample Input
  
  
banana band bee absolute acm ba b band abc
 

 

Sample Output
  
  
2 3 1 0

 
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <fstream> 

using namespace std;

int n,m,ans;     
string str; 

struct dic_tree //构造字典树,26叉树,根结点为空 
{
	dic_tree *node[26];//26个结点
	int num;//孩子节点数目;
	bool terminal;//是否结束;

	dic_tree()//构造函数,初始化;
	{
		for (int i = 0; i < 26; i++)
		{
			node[i] = NULL;
			terminal = 0;
			num = 0;
		}
	}

}*root;//树是通过指针形式来表达;

void build(dic_tree* p, string str, int end)//生成字典树;
{
	int ix = 0;
	p->num++;
	while (ix < end)
	{
		int c = str[ix]-'a';
		cout<<c<<" ";
		if (p->node[c])		//如果node[c]存在;
		{
			p=p->node[c];
			if (ix == end-1)
			{
				p->terminal = 1;
			}
			++ix;
		}
		else	//node[c]不存在;
		{
			cout<<"new dic_tree"<<endl;
			p->node[c] = new dic_tree ;//在该点生成一个26叉树

			p = p->node[c];
			if (ix == end-1)
			{
				p->terminal = 1;
			}
			++ix;
		}
		p->num++;//p相对应孩子节点增加;
	}

}

int search(dic_tree* p,string str, int end)
{
	int ix = 0;
	while (ix < end)
	{
		int c = str[ix] - 'a';
		if (p->node[c])
		{
			p = p->node[c];
			if (ix == end-1)
			{
				p->terminal = 1;
				return p->num;
			}
			ix++;
		}
		else
			return 0;
	}
}
int main()
{
	root=new dic_tree;
	ifstream infile("D:/infile.txt");
	while(getline(infile,str)) 
	{  
	   build(root,str,str.size());//生成字典树;
	   cout<<"build one word"<<endl;
	}
	//str.clear();
	cout<<"Test Word:"<<endl;
	while (cin>>str)
	{
		cout<<search(root,str,str.size())<<endl;;
	}
	return 0;
}

 
 
也可以用map存储所有可能的前缀;时间长~
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
    int i,j,k,len;
    string str;char temp[15],temp1[15];
    map <string,int> mymap;
    while(gets(temp))
    {
        if(temp[0]=='/n') break;
        len=strlen(temp);
        if(len==0) break;
        for(i=0;i<len;i++)//求出某个字符串的所有前缀,并用MAP存起来
        {
            for(j=0;j<=i;j++) temp1[j]=temp[j];temp1[j]='/0';
            str.assign(temp1);
            mymap[str]++;
        }
    }
    while(scanf("%s",&temp)!=EOF)
        cout<<mymap[temp]<<endl;//此时直接输出结果即可
    return 0;
}

Message Flood

时间限制(普通/Java):2000MS/6000MS          运行内存限制:65536KByte
总提交:139            测试通过:27

描述

Well, how do you feel about mobile phone? Your answer would probably be something like that “It’s so convenient and benefits people a lot”. However , if you ask Merlin this question on the New Year’s Eve , he will definitely answer “ What a trouble! I have to keep my fingers moving on the phone the whole night , because I have so many greeting messages to send !” . Yes , Merlin has such a long name list of his friends , and he would like to send a greeting message to each of them . What’s worse , Merlin has another long name list of senders that have sent message to him , and he doesn’t want to send another message to bother them ( Merlin is so polite that he always replies each message he receives immediately ). So , before he begins to send messages , he needs to figure to how many friends are left to be sent . Please write a program to help him.
 Here is something that you should note. First , Merlin’s friend list is not ordered , and each name is alphabetic strings and case insensitive . These names are guaranteed to be not duplicated . Second, some senders may send more than one message to Merlin , therefore the sender list may be duplicated . Third , Merlin is known by so many people , that’s why some message senders are even not included in his friend list.

输入

 There are multiple test cases . In each case , at the first line there are two numbers n and m ( 1<=n , m<=20000) , which is the number of friends and the number of messages he has received . And then there are n lines of alphabetic strings ( the length of each will be less than 10 ) , indicating the names of Merlin’s friends , one pre line . After that there are m lines of alphabetic string s ,which are the names of message senders .
 The input is terminated by n=0.

首先输入两个数m(总朋友数量),n(发送的信息数量),

接着:一系列字符串前m个表示朋友名字

n表示已发送信息的数量

输出:还有几个朋友必须发送信息

 

输出

 For each case , print one integer in one line which indicates the number of left friends he must send .

样例输入

5 3
Inkfish
Henry
Carp
Max
Jericho
Carp
Max
Carp
0

样例输出

3

 

//搜索+字典树(Trie)
//维基百科关于字典树的介绍
//Trie,又称单词查找树,是一种树形结构,是一种哈希树的变种。
//典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。
//它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
#include<cstdio>
#include<cstring>
using namespace std;
int n,m,ans;
char str[10];
struct dic_tree//构造字典树,26叉树,根结点为空
{
	dic_tree *node[26];//26个结点
	bool terminal;//表示该结点位置是否是单词的结束位
	dic_tree()//构造函数,初始化终点情况和结点
	{
		for(int i = 0;i < 26;++i)
		{
			node[i] = NULL;//初始化空结点,node[0]表示'a'在该单词中
			terminal = 0;
		}
	}
}*root;//树是通过指针形式来表达的
void build(dic_tree *p,char str[],int end)//生成字典树的函数
{
	int ix = 0;
	while(ix < end)//循环直到读完单词最后一个字母
	{
		int c = str[ix] - 'a';
		if(p->node[c])//如果到node[c]连通
		{
			p = p->node[c];
			if(ix == end - 1)
				p->terminal = 1;
			++ix;
		}
		else
		{
			p->node[c] = new dic_tree; //如果不连通,则在该结点生成一个26叉树
			p = p->node[c];
			if(ix == end - 1)
				p->terminal = 1;
			++ix;
		}
	}
}
void search(dic_tree *p,char str[],int end)
{
	int ix = 0;
	while(ix < end)
	{
		int c = str[ix] - 'a';
		if(p->node[c])
		{
			p = p->node[c];
			if(ix == end - 1 && p->terminal == 1)
			{
				--ans;
				p->terminal = 0;
				return;
			}
			++ix;
		}
		else return;
	}
}
void release(dic_tree *p)//必须递归释放结点空间
{
	 for(int i = 0;i < 26; i++)
         if(p->node[i])
              release(p->node[i]);             
	 delete p;                                 
}
void transcase(char str[])
{
	for(int i = 0;i < strlen(str);++i)
	{
		if(str[i] >= 'A' && str[i] <= 'Z')
			str[i] = str[i] - 'A' + 'a';
	}
}//转化大小写
int main()
{
	//freopen("in.txt","r",stdin);
	while(scanf("%d",&n))
	{
		if(n == 0)	break;
		scanf("%d",&m);
		root = new dic_tree;
		ans = n;
		for(int i = 0;i < n;++i)
		{
			scanf("%s",str);
			transcase(str);//转换大小写
			build(root,str,strlen(str));//生成字典树
		}
		for(int i = 0;i < m;++i)
		{
			scanf("%s",str);
			transcase(str);//转换大小写
			search(root,str,strlen(str));//搜索字典树
		}
		printf("%d/n",ans);
		release(root);//记住构造树后一定要释放结点空间,否则会内存溢出
	}
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值