字典树(trie树) 后缀树 广义后缀树
转自:http://www.cnblogs.com/dong008259/archive/2011/11/11/2244900.html
(1)字典树(Trie树)
Trie是个简单但实用的数据结构,通常用于实现字典查询。我们做即时响应用户输入的AJAX搜索框时,就是Trie开始。本质上,Trie是一颗存储多个字符串的树。相邻节点间的边代表一个字符,这样树的每条分支代表一则子串,而树的叶节点则代表完整的字符串。和普通树不同的地方是,相同的字符串前缀共享同一条分支。还是例子最清楚。给出一组单词,inn, int, at, age, adv, ant, 我们可以得到下面的Trie:
可以看出:
•每条边对应一个字母。
•每个节点对应一项前缀。叶节点对应最长前缀,即单词本身。
•单词inn与单词int有共同的前缀“in”, 因此他们共享左边的一条分支,root->i->in。同理,ate, age, adv, 和ant共享前缀”a”,所以他们共享从根节点到节点”a”的边。
•查询非常简单。比如要查找int,顺着路径i -> in -> int就找到了。
•搭建Trie的基本算法也很简单,无非是逐一把每则单词的每个字母插入Trie。插入前先看前缀是否存在。如果存在,就共享,否则创建对应的节点和边。比如要插入单词add,就有下面几步:•考察前缀”a”,发现边a已经存在。于是顺着边a走到节点a。
•考察剩下的字符串”dd”的前缀”d”,发现从节点a出发,已经有边d存在。于是顺着边d走到节点ad
•考察最后一个字符”d”,这下从节点ad出发没有边d了,于是创建节点ad的子节点add,并把边ad->add标记为d。
//copyright: www.acmerblog.com
#include <stdio.h>
#include <iostream>
using namespace std;
#define MAX 26
typedef struct TrieNode
{
int nCount; // 该节点前缀 出现的次数
struct TrieNode *next[MAX]; //该节点的后续节点
} TrieNode;
TrieNode Memory[1000000]; //先分配好内存。 malloc 较为费时
int allocp = 0;
//初始化一个节点。nCount计数为1, next都为null
TrieNode * createTrieNode()
{
TrieNode * tmp = &Memory[allocp++];
tmp->nCount = 1;
for (int i = 0; i < MAX; i++)
tmp->next[i] = NULL;
return tmp;
}
void insertTrie(TrieNode * * pRoot, char * str)
{
TrieNode * tmp = *pRoot;
int i = 0, k;
//一个一个的插入字符
while (str[i])
{
k = str[i] - 'a'; //当前字符 应该插入的位置
if (tmp->next[k])
{
tmp->next[k]->nCount++;
}
else
{
tmp->next[k] = createTrieNode();
}
tmp = tmp->next[k];
i++; //移到下一个字符
}
}
int searchTrie(TrieNode * root, char * str)
{
if (root == NULL)
return 0;
TrieNode * tmp = root;
int i = 0, k;
while (str[i])
{
k = str[i] - 'a';
if (tmp->next[k])
{
tmp = tmp->next[k];
}
else
return 0;
i++;
}
return tmp->nCount; //返回最后的那个字符 所在节点的 nCount
}
int main(void)
{
char s[11];
TrieNode *Root = createTrieNode();
while (gets(s) && s[0] != '0') //读入0 结束
{
insertTrie(&Root, s);
}
while (gets(s)) //查询输入的字符串
{
printf("%d\n", searchTrie(Root, s));
}
return 0;
}
(2)后缀树
所谓后缀树,就是包含一则字符串所有后缀的压缩了的字典树。先说说后缀的定义。给定一长度为n的字符串S=S1S2..Si..Sn,和整数i,1 <= i <= n,子串SiSi+1…Sn都是字符串S的后缀。以字符串S=XMADAMYX为例,它的长度为8,所以S[1..8], S[2..8], … , S[8..8]都算S的后缀,我们一般还把空字串也算成后缀。这样,我们一共有如下后缀。对于后缀S[i..n],我们说这项后缀起始于i。
1.S[1..8], XMADAMYX, 也就是字符串本身,起始位置为1
2.S[2..8], MADAMYX,起始位置为2
3.S[3..8], ADAMYX,起始位置为3
4.S[4..8], DAMYX,起始位置为4
5.S[5..8], AMYX,起始位置为5
6.S[6..8], MYX,起始位置为6
7.S[7..8], YX,起始位置为7
8.S[8..8], X,起始位置为8
9.空字串。记为$。
所有这些后缀字符串组成一棵字典树:
仔细观察上图,我们可以看到不少值得压缩的地方。比如蓝框标注的分支都是独苗,没有必要用单独的节点同边表示。如果我们允许任意一条边里包含多个字母,就可以把这种没有分叉的路径压缩到一条边。另外每条边已经包含了足够的后缀信息,我们就不用再给节点标注字符串信息了。我们只需要在叶节点上标注上每项后缀的起始位置。于是我们得到下图:
这样的结构丢失了某些后缀。比如后缀X在上图中消失了,因为它正好是字符串XMADAMYX的前缀。为了避免这种情况,我们也规定每项后缀不能是其它后缀的前缀。要解决这个问题其实挺简单,在待处理的子串后加一个空字串就行了。例如我们处理XMADAMYX前,先把XMADAMYX变为 XMADAMYX$,于是就得到suffix tree。
这就形成一棵后缀树了。关于如何建立一棵后缀树,已有很成熟的算法,能在o(n)时间内解决。
(3)广义后缀树
传统的后缀树只能处理一个单词的所有后缀。广义后缀树存储任意多个单词的所有后缀。例如字符串“abab”和“baba”,首先将它们使用特殊结束符链接起来,如表示成“ababbaba#”,然后求连接后的新字符的后缀树,遍历所得后缀树,如遇到特殊字符,如“”,”#”等则去掉以该节点为跟的子树,最后所得后缀树即为原字符串组的广义后缀树。其实质是将两个字符串的所有后缀,即:abab,bab,ab,b,baba#,aba#,ba#,a#,组成字典树,再进行压缩处理。广义后缀树的一个常应用就是判断两个字符串的相识度。
字典树(trie树) 后缀树 广义后缀树的更多相关文章
- D. Match & Catch 后缀自动机 || 广义后缀自动机
http://codeforces.com/contest/427/problem/D 题目是找出两个串的最短公共子串,并且在两个串中出现的次数只能是1次. 正解好像是dp啥的,但是用sam可以方便很 ...
- [转载]字典树(trie树)、后缀树
(1)字典树(Trie树) Trie是个简单但实用的数据结构,通常用于实现字典查询.我们做即时响应用户输入的AJAX搜索框时,就是Trie开始.本质上,Trie是一颗存储多个字符串的树.相邻节点间的边 ...
- 【BZOJ3926】诸神眷顾的幻想乡 【广义后缀自动机】
题意 给定一棵树,每个结点有一个颜色,问树上有多少种子串(定义子串为两点上路径颜色的序列).保证叶子结点<=20 分析 我们可以发现一个结论,任意一个子串一定是以某个叶子结点为根的trie的后缀 ...
- POJ3080 POJ3450Corporate Identity(广义后缀自动机||后缀数组||KMP)
Beside other services, ACM helps companies to clearly state their “corporate identity”, which includ ...
- 【XSY1551】往事 广义后缀数组 线段树合并
题目大意 给你一颗trie树,令\(s_i\)为点\(i\)到根的路径上的字符组成的字符串.求\(max_{u\neq v}(LCP(s_u,s_v)+LCS(s_u,s_v))\) \(LCP=\) ...
- 【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并
题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r ...
- 【bzoj5084】hashit 广义后缀自动机+树链的并+STL-set
题目描述 你有一个字符串S,一开始为空串,要求支持两种操作 在S后面加入字母C 删除S最后一个字母 问每次操作后S有多少个两两不同的连续子串 输入 一行一个字符串Q,表示对S的操作 如果第i个字母是小 ...
- CF G. Indie Album 广义后缀自动机+树链剖分+线段树合并
这里给出一个后缀自动机的做法. 假设每次询问 $t$ 在所有 $s$ 中的出现次数,那么这是非常简单的: 直接对 $s$ 构建后缀自动机,随便维护一下 $endpos$ 大小就可以. 然而,想求 $t ...
- 关于广义后缀树(多串SAM)的总结
之前我们给的SAM的例题,基本上是一个串建SAM的就能做的 如果要建多个串的SAM应该怎么做呢 首先看题,bzoj2780 我一开始的想法是SA以前的弄法,把串拼起来,中间加分隔符做SAM 这题确实可 ...
随机推荐
- Confluence 6 图片文件
当你对一个页面进行编辑的时候,选择一个图片,将会显示图片属性面板.这个面板允许你设置显示大小,添加边控和特效和链接这个图片到其他页面. 从图片的属性面板,你可以: 为以图片选择一个 显示大小(pres ...
- codevs 2597 团伙x
题目描述 Description 1920年的芝加哥,出现了一群强盗.如果两个强盗遇上了,那么他们要么是朋友,要么是敌人.而且有一点是肯定的,就是: 我朋友的 ...
- JavaScript事件兼容性写法
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- redis-sentinel 主从复制高可用
Redis-Sentinel Redis-Sentinel是redis官方推荐的高可用性解决方案,当用redis作master-slave的高可用时,如果master本身宕机,redis本身或者客户端 ...
- crm 项目的部署
发布CRM你将使用以下软件 nginx uWSGI CentOS7 CRM项目文件 virtualenv supervisor WSGI.uWSGI python web服务器开发使用WSGI协议(W ...
- vue一些注意事项
1.生命周期钩子的 this 上下文指向调用它的 Vue 实例. 不要在选项属性或回调上使用箭头函数,比如 created: () => console.log(this.a) 或 vm.$wa ...
- redhat7.2配置yum源
版权声明:声明:本博客欢迎转发,但请保留原作者信息!本站文章皆基于公开来源信息,仅代表作者个人观点 https://blog.csdn.net/wylfengyujiancheng/article ...
- mysql的双主模式配置
第一台:192.168.0.160 第二台:192.168.0.170 主从配置,第一步:192.168.0.160 作为主数据库,192.168.0.170作为从数据库,配置如下: ======== ...
- SQLServer备份计划制定
SQLServer备份计划制定 一.备份计划制定 管理-->维护计划-->维护计划向导: 可选择全库备份.差异备份.事务日志备份 为保障数据的完整性:可采用备份策略1.数据量小的场景,数据 ...
- Vs2019+openjdk12 本地Debug环境搭建过程
1. VS2019下载和安装 这个就不写了 2. cygwin安装: https://jingyan.baidu.com/article/455a99507c0b78a166277809.html 需 ...