preparing

Trie树

算法

算法简介

又称单词查找树,\(Trie\)树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

算法思路

\(Trie\)树是树的变种,样子如下:

如图是一棵\(Trie\)树。我们规定一棵\(Trie\)树有以下性质:

  • 根节点没有字符,除根节点外每一个节点都只包含一个字符。
  • 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
  • 每个节点的所有子节点包含的字符都不相同。

所以,上图的\(Trie\)树包含了\(ad,book,bus,but\)[1]四个单词(当然,尽管不存在,但是其实也可以把其它的"\(boo\)","\(bu\)"等看作单词,这个因题而异)。

代码片段

建树/存储

要建一棵\(Trie\)树,我比较喜欢用结构体,具体代码如下:

struct node{
	int s[30];//子节点的位置 
	…//题目要求的其它东西 
}a[maxl*maxn];

首先定义结构体\(a\),大小为\(maxl*maxn\),其中\(maxl\)为单词最大长度,\(maxn\)为最大单词数,开成这么大是为了防止\(RE\)(其实\(maxl*maxn\)为总字符数)。
之后,结构体中定义了\(s\)数组,用来记录子节点信息的位置。具体地,\(a_x.s_i\)表示单词中字符\(x\)之后的字符\(i-1+'a'\)在结构体中的存储位置,相当于链表,上一个字符都指向下一个字符。
最后,还定义了其它东西,看题目来定,可以是出现次数,也可以是是否出现过等。

插入单词

比如,我们要在树中插入\(boos\)[2]这个单词,该怎么办呢?

首先,我们知道,如果插入后,树应该变成这样:

也就是说,我们要在第二个\(o\)之后新建一个节点,把下一个字母\(s\)放进去。
于是,我们就有了插入的方法:从根开始,寻找第一个字母,若有则去那个节点搜下一个字母,以此类推……若没有,新建节点,直到最后一个字母结束。所以如果要加入单词\(boos\),顺序应该是这样的:

  1. 从根节点出发,找第一个字母\(b\)。找得到,前往。
  2. 从节点\(b\)找第二个字母\(o\)。找得到,前往。
  3. 从节点\(o\)找下一个字母\(o\)。找得到,前往。
  4. 从节点\(o\)找下一个字母\(s\)。找不到,新建节点\(s\)并将节点\(o\)指向\(s\)
  5. 所有字母遍历结束,插入结束。

代码如下:

void add(char x[]){
	int l=strlen(x),p=0;//p为下一个字母的位置
	for(int i=0;i<l;i++){
		if(a[p].s[x[i]-'a'+1]==0){//若没有
			a[p].s[x[i]-'a'+1]=++t;//新建节点并指向
			p=t;
		}else{//若有
			p=a[p].s[x[i]-'a'+1];
		}
	}
}

查询单词

和插入差不多,若找不到下一个字母就不存在。

例题

P2580 于是他错误的点名开始了

题意

给定\(n\)个字符串和\(m\)次询问,每次询问一个单词。对于每次询问,若给定单词中不存在输出"\(WRONG\)",若存在且是第一次询问该单词输出"\(OK\)",不是第一次输出"\(REPEAT\)"。

思路

建一棵\(Trie\)树存放单词,每个节点记录从根到该节点构成的单词是否询问过。

代码

最后的是下载的洛谷#4数据

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 10005
#define maxl 55
using namespace std;
int n,m,t=0;
char x[maxl];
struct node{
	int s[30];
	bool app;//是否出现过 
}a[maxl*maxn];
void add(){
	int l=strlen(x),p=0;
	for(int i=0;i<l;i++){
		if(a[p].s[x[i]-'a'+1]==0){
			a[p].s[x[i]-'a'+1]=++t;
			p=t;
		}else{
			p=a[p].s[x[i]-'a'+1];
		}
	}
}
void check(){
	int l=strlen(x),p=0;
	for(int i=0;i<l;i++){
		if(i==l-1){
			if(a[p].app==0){
				a[p].app=1;
				printf("OK\n");
				return;
			}else{
				printf("REPEAT\n");
				return;
			}
		}
		if(a[p].s[x[i]-'a'+1]==0){
			printf("WRONG\n");
			return;
		}else{
			p=a[p].s[x[i]-'a'+1];
		}
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		memset(x,'0',sizeof(x));
		scanf("%s",x);
		add();
	}
	scanf("%d",&m);
	for(int i=1;i<=m;i++){
		memset(x,'0',sizeof(x));
		scanf("%s",x);
		check();
	}
	return 0;
}
/*
10
fcysofnrqimlddeiapd
cetmldoyxqjars
pgghefvjjo
kchpankfwpifqjqabi
zmturfzstb
atikqwvrkrxoiuwiilr
ocibmlpeqolvg
xcjpfprvpkqul
lkitunlmdawencmhg
kjbfrmkawxeoem
20
zmturfzstb
fcysofnrqimlddeiapd
ocibmlpeqolvg
lkitunlmdawencmhg
zmturfzstb
cetmldoyxqjars
atikqwvrkrxoiuwiilr
fcysofnrqimlddeiapd
cetmldoyxqjars
fcysofnrqimlddeiapd
lkitunlmdawencmhg
kjbfrmkawxeoem
kjbfrmkawxeoem
cetmldoyxqjars
kjbfrmkawxeoem
zmturfzstb
lkitunlmdawencmhg
ocibmlpeqolvg
ocibmlpeqolvg
zmturfzstb
*/

  1. \(ad\):广告(缩写)
    \(book\):书
    \(bus\):公交车
    \(but\):但是 ↩︎

  2. \(boos\):喝倒彩(三单) ↩︎

posted @ 2021-08-15 15:26  qzhwlzy  阅读(74)  评论(0)    收藏  举报