Trie树(字典树)
Trie树,又称字典树、前缀树,是一种树形结构,是哈希树的变种,是一种用于快速检索的多叉树结构。
典型应用是用于统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高。
Trie的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的。
Trie树也有它的缺点,Trie树的内存消耗非常大。
从上面可以发现一些Trie树的特性:
1. 根节点不包含字符,除根节点外的每一个子节点都包含一个字符。
2. 从根节点到某一节点的路径上的字符连接起来,就是该节点对应的字符串。
3. 每个节点的所有子节点包含的字符都不相同。
https://www.cnblogs.com/TheRoadToTheGold/p/6290732.html
后缀数组
定义:
后缀数组(SA[i]存放排名第i大的后缀首字符下标)
名次数组(rank[i]存放各个后缀的优先级)
RANK表示你排第几 SA表示排第几的是谁 (记住这个就行)
而SA和Rank是互逆的,只要求出任意一个,另一个就可以O(Len)得到。
倍增算法
设每一轮得到的序列为rank(注意r是小写,最终后缀排名Rank是大写)。有一个很美妙的性质就出现了!第k轮的rank可由第k - 1轮的rank快速得来!
为什么呢?为了方便描述,设SubStr(i, len)为从第i个字符开始,长度为len的字符串我们可以把第k轮SubStr(i, 2k)看成是一个由SubStr(i, 2^(k−1))和SubStr(i + 2^(k−1), 2^(k−1))拼起来的东西。类似rmq算法,这两个长度而2^(k−1)的字符串是上一轮遇到过的!当然上一轮的rank也知道!那么吧每个这一轮的字符串都转化为这种形式,并且大家都知道字符串的比较是从左往右,左边和右边的大小我们可以用上一轮的rank表示,那么……这不就是一些两位数(也可以视为第一关键字和第二关键字)比较大小吗!再把这些两位数重新排名就是这一轮的rank
后缀数组模拟讲解
后缀数组de原理:在不改变排名情况下,会尽可能的减少储存的开销.
基数排序原理 : 把数字依次按照由低位到高位依次排序,排序时只看当前位。对于每一位排序时,因为上一位已经是有序的,所以这一位相等或符合大小条件时就不用交换位置,如果不符合大小条件就交换,实现可以用”桶”来做。
所以我只要每次对合并的数据进行按照从小到大排个序 用序号替换它 然后再次按照之前的步骤再次合并 再次排序替换 (什么时候结束)当全部的字符串都参与了比较就停止了
构造最长公共前缀——Height
如果一个一个数按SA中的顺序比较的话复杂度是O(N2)级别的,想要快速的得到Height就需要用到一个关于H数组的性质。
H[i] ≥ H[i - 1] - 1!
如果上面这个性质是对的,那我们可以按照H[1]、H[2]……H[Len]的顺序进行计算,那么复杂度就降为O(N)了!
让我们尝试一下证明这个性质 :
设Suffix[k]是排在Suffix[i - 1]前一名的后缀,则它们的最长公共前缀是H[i - 1]。都去掉第一个字符,就变成Suffix[k + 1]和Suffix[i]。如果H[i - 1] = 0或1,那么H[i] ≥ 0显然成立。否则,H[i] ≥ H[i - 1] - 1(去掉了原来的第一个,其他前缀一样相等),所以Suffix[i]和在它前一名的后缀的最长公共前缀至少是H[i - 1] - 1。
代码
int wa[maxn],wb[maxn],wv[maxn],ws[maxn];
int cmp(int *r , int a, int b, int l)
{
return r[a] == r[b] && r[a+l] == r[b+l];
}
void da (int *r , int *sa , int n, int m)
{
int i, j, p, *x = wa, *y = wb , *t;
for(i = 0; i < m; i++)
ws[i] = 0;
for(i = 0; i < n; i++)
ws[x[i] = r[i]]++;
for(i = 1; i < m; i++)
ws[i] += ws[i-1];
for(i = n-1; i >= 0; i--)
sa[--ws[x[i]]] = i;
for(j = 1,p = 1; p < n ; j <<= 1,m = p)
{
for(p = 0, i = n - j; i < n; i++)
y[p++]=i;
for(i = 0; i < n; i++)
if(sa[i] >= j)
y[p++] = sa[i] - j;
for(i = 0; i < n; i++)
wv[i] = x[y[i]];
for(i = 0; i < m; i++)
ws[i] = 0;
for(i = 0; i < n; i++)
ws[wv[i]]++;
for(i = 1; i < m; i++)
ws[i] += ws[i-1];
for(i = n-1; i >= 0; i--)
sa[--ws[wv[i]]] = y[i];
for(t = x,x = y,y = t,p = 1,x[sa[0]] = 0,i = 1; i < n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
}
后缀树
特点
后缀树,就是把一串字符的所有后缀保存并且压缩的字典树。相对于字典树来说,后缀树并不是针对大量字符串的,而是针对一个或几个字符串来解决问题,比如字符串的回文子串,两个字符串的最长公共子串等等。
性质:一个字符串构造了一棵树,树中保存了该字符串所有的后缀。
比如字符串的回文子串,两个字符串的最长公共子串
<1.查找某个字符串s1是否在另外一个字符串s2中
这个很简单,如果s1在字符串s2中,那么s1必定是s2中某个后缀串的前缀。
<2.指定字符串s1在字符串s2中重复的次数
<3.两个字符串S1,S2的最长公共部分(广义后缀树)
<4.最长回文串(广义后缀树)
后缀自动机
AC自动机
[HDU2896]病毒侵袭(AC自动机)
[HDU2222]Keywords Search(AC自动机)