数据结构之后缀树

本文介绍了后缀树的概念,包括如何构造后缀树,以及其与后缀数组、后缀自动机的区别。后缀数组的构建过程通过基数排序实现,并探讨了其在查找最长公共子序列(LCP)等问题上的应用。文章强调虽然后缀树功能强大,但由于效率和实现难度,后缀数组成为更实用的选择。
摘要由CSDN通过智能技术生成

一、理解:

1、后缀树,举例说明:

eg: 利用字符串prop建立后缀树。
暴力地说,可以三步建树。
第一步,得到prop的所有后缀:
B[0] = “”
B[1] = “p”,第四个后缀
B[2] = “op”,第三个后缀
B[3] = “rop”,第二个后缀
B[4] = “prop”,第一个后缀
以上五个,都是它的后缀
第二步,用这五个后缀建立trie树。
第三步,压缩:
如果一段树枝既没有分支,又没有哪一条后缀结束,它就要压缩成一个点;
如果一段树枝没有分支,但是有一条后缀在其中结束了,就先为结束点建立一个空节点,再让剩余部分压缩成点。
这样就不会把后缀也压缩掉了,压缩完就是后缀树,长这样:

建议自己先画一遍

2.后缀树与后缀数组(SA)和后缀自动机(SAM):虽然三者的前缀相同,但后缀的进化程度倍增,不是同纲。

例如后缀数组:
市面上常见两种写法:倍增(O(nlogn))和DC3(O(n))。
DC3,又称skew,y总说它的常数比倍增大,而且难写,所以一般用倍增。
倍增写法把字符串的所有后缀按字典序排序(基数排序),排完序得到的就是后缀数组SA:
SA[i]:排名第i位的是第几个后缀(不考虑空后缀)
而一般会用到的数组height:
height[i]:sa[i]和sa[i-1]的最长公共前缀
是后缀数组扩展得来的。
并且通过扩展功能,后缀数组可以逐渐向后缀树转化。

3.为什么没见人用后缀树:后缀树功能完整,构造算法也逐渐优化了。

后缀树魔法:
1.查找字符串S1是否在字符串S2中
2.计算字符串a在字符串S中的重复次数
3.查找字符串S中的最长重复子串
4.查找字符串S1和S2的最长公共子序列(LCP)
5.广义后缀树:处理多个字符串的后缀,分别加标记就行
但是功能完整就表示效率不是最优,而且它难写。

后缀树的确炫酷,但是我劝大家学后缀数组。
建树的算法学一个通宵午不一定能学会一个,而学两个小时的后缀数组,就能求LCP

二、前置技能:

基数排序O(n+k):

eg:数组A={9, 7, 5, 5, 4}
第一步,计数:
B[4]=1
B[5]=3
B[7]=4
B[9]=5
B[i]表示小于等于i的数的个数
第二步,遍历数组A并排序:
A[0]=9,排序为B[9]=5,B[9]–
A[1]=7,排序为B[7]=4,B[7]–
A[2]=5,排序为B[5]=3,B[5]–
A[3]=5,排序为B[5]=2,B[5]–
A[4]=4,排序为B[4]=1,B[4]—
注意:A[2]的5排序为3,A[3]的5排序为2。
如果第二步遍历时从后往前遍历,就变成A[2]的5排序为2,A[3]的5排序为3。

lcp的两个性质:

1.lcp[i, j]表示排名第i位的后缀和排名第j位的后缀的lcp。
那么显然有lcp[i, j] = min(lcp[i, k], lcp[k, j]),i≤k≤j。
所以lcp[i, j] = min(lcp[i, i+1], lcp[i+1, i+2], …, lcp[j-1, j])。
2.height[i] = lcp[i-1,i]
定义h[i] = height[ rk[i] ],即第i个后缀的height,即第i个后缀和排名比它小一位的后缀的lcp。
那么有h[i]≥h[i-1]-1。

三、后缀数组

输入一个字符串s,求sa数组和height数组。

先上代码:

#include<bits/stdc++.h>//acwing2715
using namespace std;
const int N=1000010;
int n,m;
char s[N];
int sa[N], x[N], y[N], c[N], rk[N], height[N];

void get_sa(){
   
	//第一轮排序,只按第一个字母排
    for(int i=
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值