trie上构建后缀数组

例题:

往事太多,有时候忘了就忘了吧。
如果有非记不可的,就只能用点附加手段啦!
我们定义一棵往事树是一个 n 个点 n-1 条边的有向无环图,点编号为 1到 n,其中 1 号点被称为是根结点,除根结点以外,
每个点都恰有一条出边(即以其作为起点的边)。
每条边上有 1 个字符(这里我们实际上用一个不大于 300的非负整数代表不同的字符),
对于任意一个点 u,u 的所有入边(即以其为终点的边)上的字符互不相同。
接下来我们定义往事,点 u 对应的往事为 u 到根的路径(显然有且只有一条)上的所有边按照从 u 到根经过的顺序排序后边上的字符依次拼接形成的字符串,简记为 r(u)。
一棵往事树的联系度取决于它包含的所有往事之中最相近的一对的相似度。具体的,我们定义 2 个点 u 和点 v 对应的往事的相似度 f(u,v)如下。
\(f(u,v)=Lcp(r(u),r(v))+Lcs(r(u),r(v))\)
其中 Lcp(a,b)表示字符串 a 和 b 的最长公共前缀的长度, Lcs(a,b)表示字符串a 和 b 的最长公共后缀的长度。
定义一棵往事树的联系度为所有满足 1<=u<v<=n 的 f(u,v)的最大值。
现在,给出一棵往事树,请你给出这棵往事树的联系度。

题解:

首先,将所有r(u)排序,这样,lcp就是相邻的r(u)的lcp的RMQ。(原理同后缀数组)
所以,排序后离得越近,lcp就越长。
显然,lcs就是lca的深度。
所以,枚举lca,即对树进行dfs,每次合并所有子树,并能询问所有间隔最近的RMQ的最大值。
用线段树合并,维护每个区间的lcp最大值,排名最大的节点,排名最小的节点,pushup时考虑左子节点的最大值和右子节点的最小值。
这步的时间复杂度是nlogn的。

考虑如何排序:

直接qsort,每次cmp二分+hash比较大小是logn的,总时间复杂度是nlog^2n的。

考虑另一种做法:

若我们已知长度为x的串的大小关系(排名),那么,我们将长度为x的串拼接在一起,就能得到长度为2x的串。
将长度为2x的串的前半部分的排名作为第一关键字,后半部分的排名作为第二关键字,排序后就能得到长度为2x的串的大小关系,进而得出长度为2x的串的排名。
然后,我们就可以继续这个过程,从而将所有r(u)排序。
由于排名是0~n的,所以可以采用基数排序,每轮时间复杂度是线性的,共logn轮,所以总时间复杂度是nlogn。
其实就是后缀数组的方法。

现在考虑求height(好像不能用后缀数组的那个方法):

方法一:二分+hash。

方法二:倍增,每次考虑x往上长度为2^i的串与y往上长度为2^i的串是否相等。
可以发现,这些串,在倍增排序时就考虑过了,若排名相等,这两个串就相等。将每次倍增排序的排名记录下来就行了。

总时间复杂度:\(O(nlogn)\)。

代码没了。

原文地址:https://www.cnblogs.com/lnzwz/p/11371720.html

时间: 2024-07-31 06:52:25

trie上构建后缀数组的相关文章

bzoj3926/luoguP3346 [Zjoi2015]诸神眷顾的幻想乡(trie上构建广义后缀自动机)

bzoj Luogu 题解时间 给你个无根trie树(你管这叫trie树?),问你选取一条有向路径能形成多少种不同字符串. 太阳花田的结构比较特殊,只与一个空地相邻的空地数量不超过20个.->只有不超过20个叶子. 纯粹看你读题的,你要是读错了这句话的含义你就白给. 如何保证完整枚举这棵树上所有可能的字符串呢? 我们以某个点为根建广义SAM,很明显每一个点都只有向根方向延展出的字符串没有被记入了SAM中. 而对于一个叶子节点,只有以它为根建SAM时,以它为起点延展出的字符串才会被记录. 而对于一

uva 10829 - L-Gap Substrings(后缀数组)

题目链接:uva 10829 - L-Gap Substrings 题目大意:给定一个字符串,问有多少字符串满足UVU的形式,要求U非空,V的长度为g. 解题思路:对字符串的正序和逆序构建后缀数组,然后枚举U的长度l,每次以长度l分区间,在l和l+d+g所在的两个区间上确定U的最大长度. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> using nam

字符串匹配(三)----后缀数组算法

一.什么是后缀数组: 字符串后缀Suffix 指的是从字符串的某个位置开始到其末尾的字符串子串.后缀数组 Suffix Array(sa) 指的是将某个字符串的所有后缀按字典序排序之后得到的数组,不过数组中不直接保存所有的后缀子串,只要记录后缀的起始下标就好了. 比如下面在下面这张图中,sa[8] = 7,表示在字典序中排第9的是起始下标为7的后缀子串,这里还有一个比较重要的数组rank,rank[i] : sa[i]在所有后缀中的排名 ,比如rk[5]=0,表示后缀下标为5的子串在后缀数组中排

poj 3693 Maximum repetition substring(后缀数组)

题目链接:poj 3693 Maximum repetition substring 题目大意:求一个字符串中循环子串次数最多的子串. 解题思路:对字符串构建后缀数组,然后枚举循环长度,分区间确定.对于一个长度l,每次求出i和i+l的LCP,那么以i为起点,循环子串长度为l的子串的循环次数为LCP/l+1,然后再考虑一下从i-l+1~i之间有没有存在增长的可能性. #include <cstdio> #include <cstring> #include <vector>

poj 3882(Stammering Aliens) 后缀数组 或者 hash

后缀数组:  构建后缀数组,注意要在字符串莫末尾加上一个没出现过的字符.然后可以2分或者直接扫描,直接扫描需要用单调队列来维护 VIEW CODE #include<cstdio> #include<algorithm> #include<iostream> #include<cmath> #include<queue> #include<stack> #include<string> #include<cstrin

Boring counting HDU - 3518 (后缀数组)

Boring counting \[ Time Limit: 1000 ms \quad Memory Limit: 32768 kB \] 题意 给出一个字符串,求出其中出现两次及以上的子串个数,要求子串之间不可以重合. 思路 对字符串后缀数组,然后枚举子串长度 \(len\),若某一段连续的 \(sa[i]\) 的 \(lcp \geq len\),那么说明这一段内存在一个长度为 \(lcp\) 的子串,而我们只需要其中的前 \(len\) 部分,接下来只要找出这个子串出现的最左和最右位置,

loj6198谢特 后缀数组+并查集+Trie

先把问题放在后缀数组上考虑 已知两个数组a b,求min(a[i],...,a[j])+(b[i]^b[j])的最大值 套路题 初始每个点都是一个小连通块 把a按从大到小的顺序加入,计算当前加入边作为min的贡献: 每次加入会把两个连通块联通,答案就是两边连通块各出一个数能得到的异或和最大值 我:这不是线性基吗 miaom:mdzz,只能有两个数 我:蛤,好难啊,怎么做啊 miaom:Trie啊 我:哦 没了 1 #include <bits/stdc++.h> 2 #define N 500

后缀数组(Suffix Array)模板及简析——Part 1:构建SA和rank数组

后缀数组(Suffix Array,SA)是处理字符串的有力工具.它比后缀树更易实现,占用空间更少,并且同样可以解决千变万化的字符串问题 首先推荐罗穗骞的论文(网上搜一下就能搜到),里面对后缀数组的定义.实现和应用都做了详细的阐述 然而不幸的是罗神犇的代码简直魔性,蒟蒻表示这代码压的根本看不懂啊…… 所以在理解了后缀数组的构建过程之后,我重新写了一份模板代码.虽然啰嗦了点(代码比较大,而且变量名故意拉长了),不过相对比较好懂 而且论文中用到的辅助空间是4N,我的模板用了3N,事实上还可以优化到只

BZOJ_2754__[SCOI2012]_喵星球上的点名_(暴力+后缀数组)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=2754 给出n个姓名串和m个点名串.求每个点名串在多少人的姓名中出现过(在名中出现或在姓中出现,不能跨越),以及最后每个人被点到多少次. 分析 这种解法是用后缀数组优化一下暴力,(优化了吗?)复杂度并不能保证,然而能A... 我们先把所有名,姓,点名串都接在一个串里面. 然后匹配问题就转化成了后缀的最长公共前缀问题. 但是由于我们讨论问题的对象是名,姓,以及点名串,所以每个部分我们用不同的分隔