转自: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树) 后缀树 广义后缀树的更多相关文章

  1. D. Match & Catch 后缀自动机 || 广义后缀自动机

    http://codeforces.com/contest/427/problem/D 题目是找出两个串的最短公共子串,并且在两个串中出现的次数只能是1次. 正解好像是dp啥的,但是用sam可以方便很 ...

  2. [转载]字典树(trie树)、后缀树

    (1)字典树(Trie树) Trie是个简单但实用的数据结构,通常用于实现字典查询.我们做即时响应用户输入的AJAX搜索框时,就是Trie开始.本质上,Trie是一颗存储多个字符串的树.相邻节点间的边 ...

  3. 【BZOJ3926】诸神眷顾的幻想乡 【广义后缀自动机】

    题意 给定一棵树,每个结点有一个颜色,问树上有多少种子串(定义子串为两点上路径颜色的序列).保证叶子结点<=20 分析 我们可以发现一个结论,任意一个子串一定是以某个叶子结点为根的trie的后缀 ...

  4. POJ3080 POJ3450Corporate Identity(广义后缀自动机||后缀数组||KMP)

    Beside other services, ACM helps companies to clearly state their “corporate identity”, which includ ...

  5. 【XSY1551】往事 广义后缀数组 线段树合并

    题目大意 给你一颗trie树,令\(s_i\)为点\(i\)到根的路径上的字符组成的字符串.求\(max_{u\neq v}(LCP(s_u,s_v)+LCS(s_u,s_v))\) \(LCP=\) ...

  6. 【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并

    题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r ...

  7. 【bzoj5084】hashit 广义后缀自动机+树链的并+STL-set

    题目描述 你有一个字符串S,一开始为空串,要求支持两种操作 在S后面加入字母C 删除S最后一个字母 问每次操作后S有多少个两两不同的连续子串 输入 一行一个字符串Q,表示对S的操作 如果第i个字母是小 ...

  8. CF G. Indie Album 广义后缀自动机+树链剖分+线段树合并

    这里给出一个后缀自动机的做法. 假设每次询问 $t$ 在所有 $s$ 中的出现次数,那么这是非常简单的: 直接对 $s$ 构建后缀自动机,随便维护一下 $endpos$ 大小就可以. 然而,想求 $t ...

  9. 关于广义后缀树(多串SAM)的总结

    之前我们给的SAM的例题,基本上是一个串建SAM的就能做的 如果要建多个串的SAM应该怎么做呢 首先看题,bzoj2780 我一开始的想法是SA以前的弄法,把串拼起来,中间加分隔符做SAM 这题确实可 ...

随机推荐

  1. MySQL数据库中的索引(一)——索引实现原理

    今天我们来探讨一下数据库中一个很重要的概念:索引. MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构,即索引是一种数据结构. 我们知道,数据库查询是数据库的最主要 ...

  2. HGOI 20190711 题解

    Problem A 矩阵第K小数 给定一个$n \times m$的矩阵,位置$A_{i,j}  = i\times j$, 给出$Q$个询问,每一次查询矩阵中第$Q_i$小的数是多少. 对于100% ...

  3. 论文阅读:Offloading Distributed Applications onto SmartNICs using iPipe

    摘要: 包含丰富计算资源的新兴多核SoC SmartNIC具有卸载通用数据中心服务器任务的潜力,但是目前尚不清楚如何有效地使用SmartNIC并最大程度地减少卸载收益,尤其是对于分布式应用程序. 为此 ...

  4. DIY Arduino 方向盘

    之前的项目中使用Arduino做UE4的输入设备时候需要用到UE4Duino这个插件,以字符串的形式从Arduino中组装信息并发送到串口,使用UE4Duino进行解析,过程比较麻烦. 最近发现的一个 ...

  5. HDU 4725 The Shortest Path in Nya Graph (最短路 )

    This is a very easy problem, your task is just calculate el camino mas corto en un grafico, and just ...

  6. php的switch函数

    PHP Switch 语句 PHP If...Else PHP While 循环 switch 语句用于基于不同条件执行不同动作. Switch 语句 如果您希望有选择地执行若干代码块之一,请使用 S ...

  7. POI 生成excel

    POI生成原生Excel-工具类     https://www.jianshu.com/p/2dfe7fe7d02e JAVA poi 帮助类  https://www.cnblogs.com/Ca ...

  8. Java多线程核心知识

    多线程相对于其他 Java 知识点来讲,有一定的学习门槛,并且了解起来比较费劲.在平时工作中如若使用不当会出现数据错乱.执行效率低(还不如单线程去运行)或者死锁程序挂掉等等问题,所以掌握了解多线程至关 ...

  9. TCP定时器 之 重传/延迟ACK/保活 定时器初始化

    创建socket时会创建传输控制块,之后调用初始化函数对控制块进行初始化,其中包括对定时器的初始化,tcp会调用tcp_init_xmit_timers函数来初始化这些定时器,本文将详细分析tcp_i ...

  10. Lognormal Distribution对数正态分布

    python机器学习-乳腺癌细胞挖掘(博主亲自录制视频) https://study.163.com/course/introduction.htm?courseId=1005269003&u ...