前缀树和TRIE树

一、前缀树

结构:用树的方式表示字符串,每个节点有p和e,p表示的是有一个字符串经过该结点,e表示的是其是否为一个字符串的终止结点。如图所示:
在这里插入图片描述
作用:用于字符串的查找和判断字符串的前缀。
代码

//结点的结构,有pass和end,还有其孩子结点,也就是边
//注意字符的值是在边上的
struct node
{
	int p;
	int e;
	//这里默认都是小写字母,所以只有26个
	//若孩子的情况比较多,那么也可以使用map 
	node* child[26];
	node()
	{
		p = 0;
		e = 0;
		memset(child,0,sizeof(child));
	}
 } ;
 
 //根据输入的字符建立前缀树
 void build(string s,node* root)
 {
 	int len = s.length();
 	//其实root->p就记录了总的有几个字符串 
 	root->p++;
 	for(int i = 0;i<len;++i)
 	{
 		if(root->child[s[i]-'a']==NULL)
 		{
 			root->child[s[i]-'a'] = new node();
		 }
		 root = root->child[s[i]-'a'];
		 root->p++;
	 }
	 root->e++;
  } 
  
  //查找某个字符串 
  bool search(string s,node* root)
  {
  	int len = s.length();
  	for(int i = 0;i<len;++i)
  	{
  		if(root->child[s[i]-'a']==NULL)
  		return false;
  		root = root->child[s[i]-'a'];
	  }
	  if(root->e>0)
	  return true;
	  return false;
	  
	  //如果是判断是否有以s为前缀的字符串,那么
	  //不用判断root->e是否大于0,直接返回true就可以了 
  }

总结:从左到右遍历字符,从上到下遍历树。

二、TRIE图(寻找子串)

作用:进行多模式匹配,也就是给定多个模式串建立Trie图,之后给定母串(被匹配串)。判断母串中是否含有模式串。

结构:首先是根据模式串建立Trie树,之后在trie上的每个节点建立前缀指针,得到trie图,前缀指针的特点是:

  1. 节点S的前缀指针指向的节点T是S对应字符串的一个子串
  2. 如果节点T是危险节点(也就是某字符串的终止结点),那么S也是危险节点

注意

  1. trie树的节点有前缀指针、孩子、危险标志(标志其是否为某字符串的终止结点)
  2. 这里建树的时候使用的数组,这对C++没有自动垃圾回收来说比较友好,毕竟使用new的时候delete是一件很头疼的事情

模板题
题目:给N个模式串,以及M个句子,判断每个句子里是否包含模式串
句子和模式串都由小写字母组成。N,M <= 1000。
每个模式串长度不超过20,每个句子长度不超过1000。

代码

//trie图结构
struct node
{
	node* child[26];
	node* prev;
	bool isDanger;
	node()
	{
		memset(child,0,sizeof(child));
		prev = NULL;
		isDanger = false;
	}
 } tree[20000];
 
 int nodeCount = 2;
 
 //根据输入的字符串建立TRIE树
 //root为tree【1】 
 void build(node* root,string s)
 {
 	int len = s.size();
 	for(int i = 0;i<len;++i)
 	{
 		if(root->child[s[i]-'a']==NULL)
 		{
 			nodeCount++;
 			root->child[s[i]-'a'] = tree+nodeCount;
		 }
		 root = root->child[s[i]-'a'];
	 }
	 root->isDanger = true;
  } 
  
  
  //建立前缀指针得到trie图
  //在trie中我们引入了一个虚拟的根结点
  //这个节点的所有孩子指向真实的root
  //这样做的原因是在字符串匹配的时候,当不匹配的时候,能够使得字符串移到下一个字符 
  void buidTrie()
  {
  	for(int i = 0;i < 26;++i)
  	tree->child[i] = tree+1;
  	tree+1->prev = tree;
  	tree->prev = NULL;
  	tree->isDanger = false;
  	queue<node*> q;
  	q.push(tree+1);
	while(!q.empty())
	{
		node* cur = q.top();
		q.pop();
		node* kid;
		for(int i = 0;i<26;++i)
		{
			kid = cur->child[i];
			if(kid)
			{
				node* Prev = cur->prev;
				while(Prev->child[i]==NULL)
				Prev = Prev->prev;
				kid->prev = Prev->child[i];
				//如果前缀指针指向的是危险节点,那么本节点也会变成危险节点 
				kid->isDanger = Prev->child[i]->isDanger;
				q.push(kid);
			}
		}
	 } 
   } 
   
//查找
bool search(string s)
{
	node* cur = tree+1;
	int len = s.size();
	for(int i = 0;i<len;++i)
	{
		//此处体现了假根结点的作用 
		while(cur->child[s[i]-'a']==NULL)
		cur = cur->prev;
		cur = cur->child[s[i]-'a'];
		if(cur->isDanger)
		return true;
	}
	return false;
 } 

要和肖战一样努力!!!!!!!!!!!
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值