字典树(trie)模板

喵喵的IDE

Time Limit: 20000/10000MS (Java/Others) Memory Limit: 512000/256000KB (Java/Others)
Problem Description

喵喵有一个神奇的IDE,这个IDE自带一个cache,还有一个当前编辑区editarea,初始的时候这个 cache 中有一个字符串 cachebegin

喵喵想打按照顺序打N个字符串 x,她可以进行三种操作。

打之前 editarea 为空
1、从cache中拿出一个字符串 B直接赋值给editarea , editarea = B.(如果不拿那么不计入操作数)
2、如果editarea里面有字符,那wuyiqi可以删除掉editarea中最后一个字符。(删光了之后就不能删除啦!)
3、喵喵可以在editarea末尾插入一个任意字符。
如果editarea的字符串恰好与她要打的第i个字符串相同,那么她就完成了这次打印,并且这个字符串自动加入cache(cache中可能出现重复字符串),然后editarea自动清空。那么喵喵就会自动进入下一个字符串的打印阶段。
她想问,对于每次字符串输出,最少需要多少次操作。
1 ≤ N ≤ 105 , 字符串长度总和 ∑(Ai) <106 , cache_begin 长度< 100

Input

第一行一个整数T,代表数据组数。

对于每组数据第一行一个整数 N 代表要打印的字符串个数,还有一个字符串cachebegin

以下N 行每行一个字符串 Ai

A和 cachebegin 都只包含小写字母

Output
对于每组测试数据输出N个数。
Sample Input
1
3 a
a
ab
abc
Sample Output
1
2
2
Hint

一开始cache中为{"a"}
第一次操作直接拿出来{"a"}就好了

现在cache为{"a","a"}
拿出来"a",加入b,两次操作。

现在cache为{"a" , "a" , "ab"}
拿出来"ab",加入c。两次操作


第一次可以从缓存中拿出字符串进行操作,删除末尾字符或者在末尾加入任意字符,使得最小步数获得新的字符串并加入缓存中。用字典树(trie)来存储字符串并进行查找,注意,字典树构建内存分配必须是连续的还是怎样。trie.v表示从当前位置到末尾还有多少个字符。


#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
struct trie
{
	trie * son[26];
	int v;
	trie()
	{
		for (int i=0;i<26;i++)
			son[i]=NULL;v=0;	
	}
}*root,k[1000010];
int t=0;
char a[1000010];
void insert(trie *rt,int l)
{
	for (int i=0;i<l;i++)
	{
		int c=a[i]-'a';
		if (rt->son[c]==NULL)
		{
			k[++t]=trie();
			rt->son[c]=&k[t];
			rt->son[c]->v=l-1-i;
		}
		else
		if (rt->son[c]->v>l-1-i)
			rt->son[c]->v=l-1-i;
		rt=rt->son[c];
	}
}
int getanswer(trie * rt,int l)
{
	int ans=l;
	for (int i=0;i<l;i++)
	{
		int c=a[i]-'a';
		if (rt->son[c]==NULL) break;
		else
		{
			if (ans>rt->son[c]->v+l-i)
				ans=rt->son[c]->v+l-i;
		}
		rt=rt->son[c];
	}
	return ans;
}
int main()
{
	int T,n;
	scanf("%d",&T);
	while (T--)
	{
		t=-1;
		scanf("%d %s",&n,a);
		k[++t]=trie();
		root=&k[t];
		int l=strlen(a);
		insert(root,l);
		while (n--)
		{
			scanf("%s",a);
			l=strlen(a);
			int ans=getanswer(root,l);
			insert(root,l);
			printf("%d\n",ans);
		}
	}
	return 0;
}

上个版本太渣了,内存容易爆栈。来个用new新建内存的方案。顺便把原题附上:http://hihocoder.com/contest/hiho2/rank

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
struct trie
{
	trie * son[30];
	int v[30];
	trie()
	{
		for (int i=0;i<30;i++)
		{	
			son[i]=NULL;
			v[i]=0;
		}
	}
}*root;
int t,n;
char s[30];
void insert(trie * rt,int l)
{
	for (int i=0;i<l;i++)
	{
		int c=s[i]-'a';
		if (rt->son[c]==NULL)
		{
			trie *rit=new trie();
			rt->son[c]=rit;
			rt->son[c]->v[c]=1;
		}
		else rt->son[c]->v[c]++;
		rt=rt->son[c];
	}
}
int getans(trie * rt,int l)
{
	int ans=0;
	for (int i=0;i<l;i++)
	{
		int c=s[i]-'a';
		if (rt->son[c]==NULL) return 0;
		else ans=rt->son[c]->v[c];
		rt=rt->son[c];
	}
	return ans;
}
/*void del(trie * root)
{
	for(int i=0;i<30;i++)
    {
        if(root->son[i]!=NULL)
            del(root->son[i]);
    }
    free(root);
}*/
int main()
{
	scanf("%d",&n);
	t=0;
	root=new trie();
	getchar();
	for (int i=0;i<n;i++)
	{
		scanf("%s",&s);
		int l=strlen(s);
		insert(root,l);
	}
	scanf("%d",&n);
	getchar();
	for (int i=0;i<n;i++)
	{
		scanf("%s",&s);
		int l=strlen(s);
		printf("%d\n",getans(root,l));
	}
	delete root;
   // del(root);
	return 0;
}



<think> 我们被要求提供字典树的C++模板代码。根据引用[2]和引用[3]的内容,我们可以整合出一个模板。 注意:引用[2]提供了结构体TrieNode和类Trie的分离实现,而引用[3]则在一个类中实现(使用child数组和isend标志)。我们将参考它们,并提供一个通用的模板模板设计思路: 1. 通常,字典树节点包含一个指针数组(每个元素指向一个子节点)和一个布尔值,表示当前节点是否为单词的结束。 2. 字典树类(Trie)包含一个根节点,并提供插入(insert)、搜索(search)和前缀搜索(startsWith)等基本操作。 根据引用[2]和引用[3]的代码,我们可以整合如下模板: - 使用结构体/类表示节点,这里我们将节点内嵌在Trie类中(也可以分开)。 - 假设我们只处理26个小写字母,所以子节点数组大小为26。 我们将实现一个包含以下功能的模板: - 插入一个字符串(insert) - 搜索一个完整的字符串(search) - 搜索前缀(startsWith) 注意:引用[2]的代码中,Trie类有根节点,插入操作已经给出,但没有给出搜索和前缀搜索。引用[3]提供了插入操作,但同样没有其他操作。因此,我们需要补充完整。 下面是一个完整的字典树模板代码(C++): </think> ### 字典树Trie)C++模板代码 以下是根据站内引用整合的标准字典树模板,支持插入、搜索和前缀匹配操作,适用于26个小写英文字母场景: ```cpp #include <vector> #include <string> using namespace std; const int TRIE_NODE_SIZE = 26; // 子节点数量(26个小写字母) class Trie { private: struct TrieNode { vector<TrieNode*> next; // 子节点指针数组 bool isEnd; // 单词结束标记 TrieNode() : next(TRIE_NODE_SIZE, nullptr), isEnd(false) {} }; TrieNode* root; // 根节点 public: Trie() : root(new TrieNode()) {} // 插入单词 void insert(string word) { TrieNode* node = root; for (char ch : word) { int idx = ch - 'a'; if (node->next[idx] == nullptr) { node->next[idx] = new TrieNode(); } node = node->next[idx]; } node->isEnd = true; // 标记单词结束[^1][^3] } // 搜索完整单词 bool search(string word) { TrieNode* node = root; for (char ch : word) { int idx = ch - 'a'; if (node->next[idx] == nullptr) return false; node = node->next[idx]; } return node->isEnd; // 必须匹配到单词末尾[^2] } // 搜索前缀 bool startsWith(string prefix) { TrieNode* node = root; for (char ch : prefix) { int idx = ch - 'a'; if (node->next[idx] == nullptr) return false; node = node->next[idx]; } return true; // 前缀存在即可[^1] } }; ``` ### 核心特性说明 1. **空间换时间**:通过公共前缀减少字符串比较次数,查询效率$O(L)$($L$为字符串长度)[^1] 2. **动态建树**:按需创建子节点,避免预分配空间浪费 3. **前缀优化**:`startsWith()`利用前缀共享特性快速判断 ### 使用示例 ```cpp int main() { Trie trie; trie.insert("apple"); trie.search("apple"); // 返回 true trie.search("app"); // 返回 false trie.startsWith("app"); // 返回 true trie.insert("app"); trie.search("app"); // 返回 true } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值