字典树(trie)——杨子曰算法

#字典树(trie)——杨子曰算法
先扔一道题:HDU – 1251统计难题
就是说给你一堆字符串,再是一堆询问,问你以这个字符串为前缀的字符串有多少个?

今天我们来曰一个字符串中常用的数据结构——字典树(高雅的人称之为trie树(读作:踹树))
trie树有一下几个特点:
1.根节点是空的
2.每个节点上都会记录一个字符(除了根节点)
3.从更节点下面出发,往下走路径上记录字符串,So,两个串相同的前缀是它们后缀的公共祖先
我都觉得我没有讲清楚,相信你也一脸懵逼,于是我们来搞一个例子:
假设我们要把一下字符串放进trie:

aab
ab
abbc
abba
ac
bbb
bbc
ba
bac
cca

我们可以得到这样一棵trie树:
蓝色的节点表示这个节点是一个字符串的结尾,也就是从根到蓝色节点的路径上是是一个串
《字典树(trie)——杨子曰算法》
只要你是地球人就能秒懂
那我们怎么建树呢?,可以说是非常滴简单呀!!直接上代码:

                                      //tr[k][p]表示节点k的字符为p的儿子的编号
void build(string s){                 //我们要把s放进trie里
	int k=1;                          //根节点是1,我们现在在k节点
	for (int i=0;s[i];i++){
		int p=s[i]-'a';
		if (!tr[k][p]) tr[k][p]=++sum;//如果这个节点不存在,那就建一个,并把当前节点的这条边指向它
		k=tr[k][p];                   //继续往下走
	}
}

很显然trie能解决的问题都更前缀有很大关系,解决问题时我们要考虑在trie上是否要记录东西?记录什么东西?
这些东西可以是:这个节点是不是一个串的结尾,这个节点经过了几次……(我也一时半会儿举不出,尴尬)
到现在为止,我们会发现上面那个问题会变得灰常简单了,我们要在每个节点记录它在建树是时经过的次数,也就是所有字符串中,以根节点到这个节点路径上的字符串为前缀的字符串个数(←,很绕,自己模拟一下)
Then,对于每次查询我们从根出发,这到这个字符串末尾的节点,输出节点上记录的值就欧了。
代码走起:

int find(char *s){
	int k=1;
	for (int i=0;s[i];i++){
		int p=s[i]-'a';
		if (!tr[k][p]) return 0;//这个前缀不存在
		k=tr[k][p];
	}
	return num[k];
}

总结一波:建树复杂度O(n|s|),查询复杂度O(|s|),可以说是非常优啊!
OK,完事

c++代码:

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

char s[100];
int sum=1;
int tr[500005][30],num[500005];

void build(char *s){
	int k=1;
	for (int i=0;s[i];i++){
		int p=s[i]-'a';
		if (!tr[k][p]) tr[k][p]=++sum;
		k=tr[k][p];
		num[k]++;
	}
}

int find(char *s){
	int k=1;
	for (int i=0;s[i];i++){
		int p=s[i]-'a';
		if (!tr[k][p]) return 0;
		k=tr[k][p];
	}
	return num[k];
}

int main(){
	while(gets(s) && s[0]) build(s);
	while(~scanf("%s",&s)){
		cout<<find(s)<<endl;
	}
	return 0;
}

注意:
对于有些题目,你要考虑把哪些字符串放进trie,推荐一道题:POJ – 1204 Word Puzzles
你以为字典树只能解决字符串的问题吗?推荐一道题:HDU 4825 – Xor Sum
于XJ机房607

    原文作者:Trie树
    原文地址: https://blog.csdn.net/HenryYang2018/article/details/81452881
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞