字典树(Trie)

字典树是一种用于字符串处理的高级数据结构。

字典树的形态不是固定的,它是根据需要处理的不同字符的数量来决定的。对于某种场合,有n种不同的字符,则对应的字典树就是n叉树。举个例子,假设需要处理的字符串只有小写字母组成,则一共有26种字符,此时构造出来的字典树就是一棵26叉树,即每个结点有26个子结点。

边:字典树上的每条边代表一个字符。

结点:字典树上的每个结点中包含了它的所有孩子的指针,以及从根结点到当前结点的沿途所得到的字符构成的字符串的出现次数。

初始化:一开始字典树只有根结点,它的所有孩子都指向空。

插入字符串:从根结点开始,每读取一个原字符串中的内容,就判断对应的子结点是否存在,若不存在,则为它开辟空间。然后将当前结点指向该子结点,这样就表示走过了一个字符,该结点与它的子结点之间的边就代表该字符。这样一直往下走,直到字符串遍历完。把最后一个结点的出现次数加上1,表示该字符串出现次数增加一次。

查询字符串出现次数:从根结点开始往下走,若在字符串遍历完之前发现某个对应子结点已经不存在,则说明字典树上没有存储该字符串,直接返回0表示没有出现。否则返回最后一个结点上的出现次数即可。


#include <iostream>
#include <cstdio>
#include <string>
using namespace std;

struct Node
{
	int count;
	int next[26];
	void init()
	{
		count = 0;
		for (int i = 0; i < 26; ++i)
		{
			next[i] = -1;
		}
	}
};

Node node[100005];
int cur;

void initTrie()
{
	cur = 0;
	node[0].init();
}

void insertString(string str)
{
	int p = 0;
	int t = 0;
	for (int i = 0; i < str.size(); ++i)
	{
		t = str[i] - 'a';
		if (node[p].next[t] == -1)
		{
			node[p].next[t] = ++cur;
			node[cur].init();
		}
		p = node[p].next[t];
	}
	++node[p].count;
}

int searchString(string str)
{
	int p = 0;
	int t = 0;
	for (int i = 0; i < str.size(); ++i)
	{
		t = str[i] - 'a';
		if (node[p].next[t] == -1) return 0;
		p = node[p].next[t];
	}
	return node[p].count;
}

int main()
{
	string str;
	int n, q;
	initTrie();
	cin >> n;	//输入需要插入字典树的字符串的个数
	while (n--)
	{
		cin >> str;
		insertString(str);	//将字符串插入字典树中
	}
	cin >> q;	//输入需要查询的字符串的个数
	while (q--)
	{
		cin >> str;
		cout << searchString(str) << endl;	//输出该字符串的出现次数
	}
	return 0;
}

字典树的应用除了上述的查找字符串出现次数之外,还可以用于其他一些类似情况,比如对于多个字符串,要找出以某个字符串为前缀的字符串的个数等。

另外,字典树可以使用静态实现或动态实现,所谓静态实现就是上述代码中使用的方法,先开辟大量的结点空间,然后设置一下指针指向的结点号即可。动态实现则是指不提前开辟空间,当需要的时候再动态申请空间。

最坏情况下,字典树需要的空间=所有字符串的长度之和*每个结点所用空间,而查找某个字符串出现次数的时间仅为O(L),L为所查询的字符串的最大长度。

你可能感兴趣的:(字典树(Trie))