Faster RepetitionAware Compressed Suffix Trees based on Block Trees^{†}^{†}thanks: Partially funded by Fondecyt Grant 1170048 and by Basal Funds FB0001, Conicyt. Chile.
Abstract
Suffix trees are a fundamental data structure in stringology, but their space usage, though linear, is an important problem for its applications. We design and implement a new compressed suffix tree targeted to highly repetitive texts, such as large genomic collections of the same species. Our suffix tree builds on Block Trees, a recent LempelZivbounded data structure that captures the repetitiveness of its input. We use Block Trees to compress the topology of the suffix tree, and augment the Block Tree nodes with data that speeds up suffix tree navigation.
Our compressed suffix tree is slightly larger than previous repetitionaware suffix trees based on grammars, but outperforms them in time, often by orders of magnitude. The component that represents the tree topology achieves a speed comparable to that of generalpurpose compressed trees, while using 2.3–10 times less space, and might be of interest in other scenarios.
Keywords:
Compressed suffix trees Compressed data structures LempelZiv compression Repetitive text collections Bioinformatics1 Introduction
Suffix trees [40, 24, 39] are one of the most appreciated data structures in Stringology [3] and in application areas like Bioinformatics [14], enabling efficient solutions to complex problems such as (approximate) pattern matching, pattern discovery, finding repeated substrings, computing matching statistics, computing maximal matches, and many others. In other collections, like natural language and software repositories, suffix trees are useful for plagiarism detection [25], authorship attribution [41], document retrieval [15], and others.
While their linear space complexity is regarded as acceptable in classical terms, their actual space usage brings serious problems in application areas. From an Information Theory standpoint, on a text of length over alphabet , classical suffix tree representations use bits, whereas the information contained in the text is, in the worst case, just bits. From a practical point of view, even carefully engineered implementations [18] require at least bytes per symbol, which forces many applications to run the suffix tree on the (orders of magnitude slower) secondary memory.
Consider for example Bioinformatics, where various complex analyses require the use of sophisticated data structures, suffix trees being among the most important ones. DNA sequences range over different nucleobases represented with bits each, whereas the suffix tree uses at least bytes = bits per base, that is, of the text size. A human genome fits in approximately MB, whereas its suffix tree requires about GB. The space problem becomes daunting when we consider the DNA analysis of large groups of individuals; consider for example the 100,000humangenomes project (www.genomicsengland.co.uk).
One solution to the problem is to build suffix trees on secondary memory [8, 10]. Most suffix tree algorithms, however, require traversing them across arbitrary access paths, which makes secondary memory solutions many orders of magnitude slower than in main memory. Another approach replaces the suffix trees with suffix arrays [23], which decreases space usage to bytes (32 bits) per character but loses some functionality like the suffix links, which are essential to solve various complex problems. This functionality can be recovered [2] by raising the space to about bytes (48 bits) per character.
A promising line of research is the construction of compact representations of suffix trees, named Compressed Suffix Trees (CSTs), which simulate all the suffix tree functionality within space bounded not only by bits, but by the information content (or text entropy) of the sequence. An important theoretical achievement in this line was a CST using bits on top of the text entropy that supports all the operations within an time penalty factor [37]. A recent implementation [31] uses, on DNA, about 10 bits per base and supports the operations in a few microseconds. While even smaller CSTs have been proposed, reaching as little as 5 bits per base [35], their operation times raise until the milliseconds, thus becoming nearly as slow as a secondarymemory deployment.
Despite the progress done, further space reductions are desirable when facing large genome repositories. Fortunately many of the largest text collections are highly repetitive; for example DNA sequences of two humans differ by less than [38]. This repetitiveness is not well captured by statistical based compression methods [17], on which most of the CSTs are based. LempelZiv [21] and grammar [16] based compression techniques, among others, do better in this scenario [26], but only recently we have seen CSTs building on them, both in theory [12, 5] and in practice [1, 28]. The most successful CSTs in practice on repetitive collections are the grammarcompressed suffix trees (GCSTs), which on DNA use about 2 bits per base and support the operations in tens to hundreds of microseconds.
GCSTs use grammar compression on the parentheses sequence that represents the suffix tree topology [34], which inherits the repetitiveness of the text collection. While LempelZiv compression is stronger, it does not support easy access to the sequence. In this paper we explore an alternative to grammar compression called Block Trees [7, 32], which offer similar approximation ratios to LempelZiv compression, but promise faster access.
Our main algorithmic contribution is the BTCT, a BlockTreebased representation of tree topologies, which enriches Block Trees in order to support the required navigation operations. Although we are unable to prove useful upper bounds on the operation times, the BTCT performs very well in practice: while using 0.3–1.5 bits per node in our repetitive suffix trees, it implements the navigation operations in a few microseconds, becoming very close to the performance of plain 2.8bitpernode representations that are blind to repetitiveness [29]. We use the BTCT to represent suffix tree topologies in this paper, but it might also be useful in other scenarios, such as representing the topology of repetitive XML collections [4].
As said, our new suffix tree, BTCST, uses the BTCT to represent the suffix tree topology. Although larger than the GCST, it still requires about 3 bits per base in highly repetitive DNA collections. In exchange, it is faster than the GCST, often by an order of magnitude. This owes to the BTCT directly, but also indirectly: Its faster navigation enables the binary search for the “child by letter” operation in suffix trees, which is by far the slowest one. While with the GCST a linear traversal of the children is advisable [28], a binary search pays off in the BTCST, making it faster especially on large alphabets.
2 Preliminaries and Related Work
A text is a sequence of symbols over an alphabet , terminated by a special symbol that is lexicographically smaller than any symbol of . A substring of is denoted . A substring is a prefix if and a suffix if .
The suffix tree [40, 24, 39] of a text is a trie of its suffixes in which unary paths are collapsed into a single edge. The tree then has less than nodes. The suffix tree supports a set of operations (see Table 1) that suffices to solve a large number of problems in Stringology [3] and Bioinformatics [14].
Operation  Description 

root()  The root of the suffix tree 
isleaf()  True if is a leaf node 
firstchild()  The first child of in lexicographical order 
treedepth()  The number of edges from root() to 
nextsibling()  The next sibling of in lexicographical order 
previoussibling()  The previous sibling of in lexicographical order 
parent()  The parent of 
isancestor(,)  True if is ancestor of 
levelancestor(,)  The ancestor of at tree depth 
lca(,)  The lowest common ancestor between and 
letter(, )  str()[] 
stringdepth()  str() 
suffixlink()  The node s.t. str() = str()[2,stringdepth()] 
stringancestor(,)  The highest ancestor of s.t. stringdepth() 
child(,)  The child of s.t. str()[stringdepth()+1] = 
The suffix array [23] of a text is a permutation of such that is the starting position of the th suffix in increasing lexicographical order. Note that the leaves descending from a suffix tree node span a range of suffixes in .
The function is the length of the longest common prefix (lcp) of strings and . The LCP array [23], , is defined as and for all , that is, it stores the lengths of the longest common prefixes between lexicographically consecutive suffixes of .
2.1 Succinct tree representations
A balanced parentheses (BP) representation (there are others [34]) of the topology of an ordinal tree of nodes is a binary sequence (or bitvector) built as follows: we traverse in preorder, writing an opening parenthesis (a bit 1) when we first arrive at a node, and a closing one (a bit 0) when we leave its subtree. For example, a leaf looks like “10”. The following primitives can be defined on :
These primitives suffice to implement a large number of tree navigation operations, and can all be supported in constant time using bits on top of [29]. These include the operations needed by suffix trees. For example, interpreting nodes as the position of their opening parenthesis in , it holds that , and the lowest common ancestor of two nodes is .
2.2 Compressed Suffix Arrays
A milestone in the area was the emergence of Compressed Suffix Arrays (CSAs) [27], which using space proportional to that of the compressed sequence managed to answer access queries to the original suffix array and its inverse (i.e., return any and ), to the indexed sequence (i.e., return any ), and access to a novel array, , which lets us move from a text suffix to the next one, , yet indexing the suffixes by their lexicographic rank, . This function plays an important role in the design of CSTs, as we see next.
2.3 Compressed Suffix Trees
Sadakane [37] designed the first CST, on top of a CSA, using bits and solving all the suffix tree operations in time . He makes up a CST from three components: a CSA, for which he uses his own proposal [36]; a BP representation of the suffix tree topology, using at most bits; and a compressed representation of , which is a bitvector encoding the array (i.e., the LCP array in text order). A recent implementation [31] of this index requires about bits per character and takes a few microseconds per operation.
Russo et al. [35] managed to use just bits on top of the CSA, by storing only a sample of the suffix tree nodes. An implementation of this index [35] uses as little as 5 bits per character, but the operations take milliseconds, as slow as running in secondary storage.
Yet another approach [11] also obtains on top of a CSA by getting rid of the tree topology and expressing the tree operations on the corresponding suffix array intervals. The operations now use primitives on the LCP array: find the previous/next smaller value (psv/nsv) and find minima in ranges (rmq). They also noted that bitvector contains runs, where is the number of runs of consecutive increasing values in , and used this fact to runlength compress . Abeliuk et al. [1] designed a practical version of this idea, obtaining about bits per character and getting a time performance of hundreds of microseconds per operation, an interesting tradeoff between the other two options.
Engineered adaptations of these three ideas were implemented in the SDSL library [13], and are named cst_sada, cst_fully, and cst_sct3, respectively. We will use and adapt them in our experimental comparison.
2.4 Repetitionaware Compressed Suffix Trees
Abeliuk et al [1] also presented the first CST for repetitive collections. They built on the third approach above [11], so they do not represent the tree topology. They use the RLCSA [22], a repetitionaware CSA with size proportional to , which is very low on repetitive texts. They use grammar compression on the differential LCP array, . The nodes of the parsing tree (obtained with RePair [20]) are enriched with further data to support the operations psv/nsv and rmq. To speed up simple LCP accesses, the bitvector is also stored, whose size is also proportional to . Their index uses 1–2 bits per character on repetitive collections. It is rather slow, however, operating within (many) milliseconds.
Navarro and Ordóñez [28] include again the tree topology. Since text repetitiveness induces isomorphic subtrees in the suffix tree, they grammarcompressed the BP representation. The nonterminals are enriched to support the tree navigation operations enumerated in Section 2.1. Since they do not need psv/nsv/rmq operations on LCP, they just use the bitvector , which has a few runs and thus is very small. Their index uses slightly more space, closer to 2 bits per character, but it is up to 3 orders of magnitude faster than that of Abeliuk et al. [1]: their structure operates in tens to hundreds of microseconds per operation, getting closer to the times of generalpurpose CSTs.
It is worth mentioning the recent work by Farruggia et al. [9], who builds on Relative LempelZiv [19] to compress the suffix trees of the individual sequences (instead of that of the whole collection). They showed to be time and spacecompetitive against the CSTs mentioned, but their structure offers a different functionality (useful for other problems). Some recent theoretical work includes Gagie et al.’s [12] bits CST using RunLength ContextFree Grammars [30] on the LCP and the suffix arrays, and Belazzougui and Cunial’s [5] CST based on the CDAWG [6] (a minimized automaton that recognizes all the substrings of ). Both support most operations in time .
3 Block Trees
A Block Tree [7] is a full ary tree that represents a (repetitive) sequence in compressed space while offering access and other operations in logarithmic time. The nodes at depth (the root being depth 0) represent blocks of of length , where we pad to ensure these numbers are integers. Such a node , representing some block , can be of three types:
 LeafBlock:

If , where is a parameter, then is a leaf of the Block Tree, and it stores the string explicitly.
 BackBlock:

Otherwise, if and are not their leftmost occurrences in , then the block is replaced by its leftmost occurrence in : node stores a pointer to the node such that the first occurrence of starts inside , more precisely it occurs in . This offset inside is stored at . Node is not considered at deeper levels.
 InternalBlock:

Otherwise, the block is split into equal parts, handled in the next level by the children of . The node then stores a pointer to its children.
The Block Tree can return any by starting at position in the root block. Recursively, the position is translated in constant time into an offset inside a child node (for InternalBlocks), or inside a leftward node in the same level (for BackBlocks, at most once per level). At leaves, the symbol is stored explicitly. Setting and removing the first levels, where is the size of the LempelZiv parse of , the following properties hold: (1) The height of the Block Tree is . (2) The Block Tree uses bits of space. (3) The target nodes of BackBlocks are always InternalBlocks, and thus access takes time.
If we augment the nodes of the Block Tree with rank information for the symbols of the alphabet, the Block Tree now uses bits and can answer rank and select queries on in time . Specifically, for every , we store in every node the number of s in . Further, every BackBlock node pointing to stores the number of s in .
Our new repetitionaware CST will represent the BP topology with a Block Tree. Since in this case and , the Block Tree will use bits of space and answer , , and in time . In the next section we show how to solve the missing operations (, , , and ) by storing further data on the Block Tree nodes, yet preserving the bits space.
4 Our RepetitionAware Compressed Suffix Tree
Following the scheme of Sadakane [37] we propose a threecomponent structure to implement a new CST tailored to highly repetitive inputs. We use the RLCSA [22] as our CSA. For the LCP, we use the compressed version of the bitvector [11]. For the topology, we use BP and represent the sequence with a Block Tree, adding new fields to the Block Tree nodes to efficiently answer all the queries we need (Section 2.1). We call this representation Block Tree CST (BTCST). Section 4.1 describes BTCT, our extension to Block Trees, and Section 4.2 our improved operation child() for the BTCST.
4.1 Block Tree Compressed Topology (BTCT)
We describe our main data structure, Block Tree Compressed Topology (BTCT), which compresses a parentheses sequence and supports navigation on it.
4.1.1 Block Tree augmentation
Stored fields. We augment the nodes of the Block Tree with the following fields:

For every node that represents the block :

, the number of 1s in , that is, in .

lrank (leaf rank), the number of s (i.e., leaves in BP) that finish inside , that is, in .

lbreaker (leaf breaker), a bit telling whether the first symbol of is a and the preceding symbol in is a , that is, whether .

mexcess, the minimum excess reached in , that is, in .


For every BackBlock node that represents and points to its first occurrence inside with offset :

, the number of 1s in the prefix of contained in (, the first block spanned by ), that is, in .

fblrank, the number of s that finish in , that is, in .

fblbreaker, a bit telling whether the first symbol of is a and the preceding symbol is a , that is, whether .

fbmexcess, the minimum excess reached in , that is, .

mfb, a bit telling whether the minimum excess of is reached in , that is, whether .

Fields computed on the fly. In the description of the operations we will use other fields that are computed in constant time from those we already store:

For every node that represents

, the number of s in , that is, .

excess, the excess of 1s over 0s in , that is, .


For every BackBlock node that represents and points to its first occurrence inside with offset :

, the number of 0s in , that is, .

, the number of 0s1s in the prefix of that precedes (), that is, .

fbexcess, the excess in , that is, .

sbexcess, the excess in (2nd block spanned by ), that is, .

pfblrank, the number of s that finish in , that is, .

sbmexcess, the minimum excess in , that is, in . We store either or , the one that differs from . To deduce the nonstored field we use mexcess, fbexcess and mfb.

4.1.2 Complex operations
Apart from the basic operations solved in the original Block Tree we need, as described in Section 2.1, more sophisticated ones to support navigation in the parentheses sequence.
and . The implementations of these operations are analogous to those for and respectively, in the base Block Tree. The only two differences are that in LeafBlocks we consider the lbreaker field to check whether the block starts with a leaf, and in BackBlocks we consider fields lbreaker and fblbreaker to check whether we have to add or remove one leaf when moving to a leftward node. Like and , our operations work per level, and then have their same time complexity, given in Section 3.
and . We only show how to solve with ; the other cases are similar (some combinations not needed for our CST require further fields). Thus we aim to find the smallest position where the excess of is .
We describe our solution as a recursive procedure with two global variables: from the input, and . Variables and are the limits of the search for the currently processed node, and is the accumulated excess of the part of the range that has already been processed. The procedure is initially called at the Block Tree root with and with . If at some point reaches , we have found the answer to the search. The general idea is to traverse the range of the current node left to right, using the fields , and to speed up the procedure:

If the search range spans the entire block (i.e., ) and the answer is not reached inside (i.e., ), then we increase by and return .

If is a LeafBlock we scan bitwise, increasing for each 1 and decreasing for each 0. If reaches at some index , we return ; otherwise we return .

If is an InternalBlock, we identify the th child of , which contains position , and the th, which contains position (it could be that ). We then call fwdsearch recursively on the th to the th children, intersecting the query range with the extent of each child (the search range will completely cover the children after the th and before the th). As soon as any of these calls returns a non value, we adjust (i.e., shift) and return it. If all of them return , we also return .

If is a BackBlock we must translate the query to the original block , which starts at offset in , where . We first check whether the query covers the prefix of contained in , (i.e., if and ). If so, we check whether we can skip , namely if . If we can skip it, we just update to , otherwise we call fwdsearch recursively on the intersection of and the translated query range. If the answer is not , we adjust and return it. Otherwise, we turn our attention to the node next to . Again, we check whether the query covers the suffix of contained in , (i.e., and ). If so, we check whether we can skip , namely if . If we can skip it, we just update to , otherwise we call fwdsearch recursively on the intersection of and the translated query range. If the answer is not , we adjust and return it. Otherwise, we return .
. This operation seeks the minimum excess in . We will also start at the root with the global variable set to zero. A local variable will keep track of the minimum excess seen in the current node, and will be initialized at in each recursive call. The idea is the same as for fwdsearch: traverse the node left to right and use the fields , and to speed up the traversal.

If the query covers the entire block (i.e., ), we increase by and return .

If is a LeafBlock we record the initial excess in and scan bitwise, updating for each bit read as in operation fwdsearch. Every time we have , we update . At the end of the scan we return .

If is an InternalBlock, we identify the th child of , which contains position , and the th, which contains position (it could be that ). We then call minexcess recursively on the th to the th children, intersecting the query range with the extent of each child (the search range will completely cover the children after the th and before the th, so these will take constant time). We return the minimum between all their answers (composed with their correspondent prefix excesses).

If is a BackBlock we translate the query to the original block , which starts at offset in , where . We first check whether the query covers the prefix of contained in , (i.e., if and ). If so, we simply set and update to . Otherwise we call minexcess recursively on the intersection of and the translated query range, and record its answer in . We now consider the block next to and again check whether the query covers the suffix of contained in , (i.e., if and ). If so, we just set and update to . Otherwise, we call minexcess on the intersection of and the translated query range, record its answer in , and set . Finally, we return .
Note that, although we look for various opportunities of using the precomputed data to skip parts of the query range, the operations fwdsearch, bwdsearch, and minexcess are not guaranteed to work proportionally to the height of the Block Tree. The instances we built that break this time complexity, however, are unlikely to occur. Our experiments will show that the algorithms perform well in practice.
4.2 Operation child
The fast operations enabled by our BTCT structure give space for an improved algorithm to solve operation child(). Previous CSTs first compute and then linearly traverse the children of from with operation nextsibling, checking for each child whether , and stopping as soon as we find or exceed . Since computing letter is significantly more expensive than our nextsibling, we consider the variant of first identifying all the children of , and then binary searching them for , using letter. We then perform operations nextsibling, but only operations letter.
5 Experiments and Results
We measured the time/space performance of our new BTCST and compared it with the state of the art.
Our code and testbed will be available at
https://github.com/elarielcl/BTCST.
5.1 Experimental setup
Compared CSTs. We compare the following CST implementations.
 BTCST.

Our new Compressed Suffix Tree with the described components. For the BTCT component we vary and .
 GCST.

The Grammarbased Compressed Suffix Tree [28]. We vary parameters rulesampling and Csampling as they suggest.
 CST_SADA ,CST_SCT3, CST_FULLY.

Adaptation and improvements from the SDSL library^{1}^{1}1Succinct data structures library (SDSL), https://github.com/simongog/sdsllite on the indexes of Sadakane [37], Fischer et al. [11] and Russo et al. [35], respectively. CST_SADA maximizes speed using Sadakane’s CSA [36] and a noncompressed version of bitvector . CST_SCT3 uses instead a Huffmanshaped wavelet tree of the BWT as the suffix array, and a compressed representation [33] for bitvector and those of the wavelet tree. This bitvector representation exploits the runs and makes the space sensitive to repetitiveness, but it is slower. CST_FULLY uses the same BWT representation. For all these suffix arrays we set and .
 CST_SADA_RLCSA, CST_SCT3_RLCSA.
For the CSTs using the RLCSA, we fix their parameters to for the sampling of and for the text sampling. We only show the Paretooptimal results of each structure. Note that we do not include the CST of Abeliuk et al. [1] in the comparison because it was already outperformed by several orders of magnitude by GCST [28].
Text collection and queries. Our input sequences come from the Repetitive Corpus of Pizza&Chili (http://pizzachili.dcc.uchile.cl/repcorpus). We selected einstein, containing all the versions (up to January 12, 2010) of the German Wikipedia Article of Albert Einstein (89MB, compressible by p7zip to 0.11%); influenza, a collection of 78,041 H. influenzae genomes (148MB, compressible by p7zip to 1.69%); and kernel, a set of 36 versions of the Linux Kernel (247MB, compressible by p7zip to 2.56%).
Computer. The experiments ran on an isolated Intel(R) Xeon(R) CPU E52407 @ 2.40GHz with 256GB of RAM and 10MB of L3 cache. The operating system is GNU/Linux, Debian 2, with kernel 4.9.08amd64. The implementations use a single thread and all of them are coded in C++. The compiler is gcc version 4.6.3, with O9 optimization flag set (except Cst_sada, Cst_sct3 and Cst_fully, which use their own set of optimization flags).
Operations. We implemented all the suffix tree operations of Table 1. From those, for lack of space, we present the performance comparison with other CSTs on five important operations: nextsibling, parent, child, suffixlink, and lca.
To test our suffix tree in more complex scenarios we implemented the suffixtreebased algorithm to solve the “maximal substrings” problem [28] on all of the above implementations except for CST_FULLY (because of its poor time performance). We use their same setup [28], that is, influenza from Pizza&Chili as our larger sequence and a substring of size ( and MB) of another influenza sequence taken from https://ftp.ncbi.nih.gov/genomes/INFLUENZA. BTCST uses and and GCST uses rulesampling and Csampling . The tradeoffs refer to sasampling for the RLCSAs.
5.2 Results and discussion
Figure 1 shows the space and time for all the indexes and all the operations. The smallest structure is GCST, which takes as little as 0.5–2 bits per symbol (bps). The next smallest indexes are BTCST, using 1–3 bps, and CST_FULLY, using 2.0–2.5 bps. The compressed indexes not designed for repetitive collections use 4–7 bps if combined with a RLCSA, and 6–10.5 bps in their original versions (though we also adapted the bitvectors of CST_SCT3).
From the BTCST space, component takes just 2%–9%, the RLCSA takes 23%–47%, and the rest is the BTCT (using a sweetpoint configuration). This component takes 0.30 bits per node (bpn) on einstein, 1.06 bpn on influenza, and 1.50 bpn on kernel. The grammarcompressed topology of GCST takes, respectively, 0.05, 0.81, and 0.39 bpn.
In operations nextsibling and parent, which rely most heavily on the suffix tree topology, our BTCT component building on Block Trees makes BTCST excel in time: The operations take nearly one microsecond (sec), at least 10 times less than the grammarbased topology representation of GCST. CST_FULLY is three orders of magnitude slower on this operation, taking over a millisecond. Interestingly, the larger representations, including those where the tree topology is represented using 2.79 bits per node (CST_SADA[_RLCSA]), are only marginally faster than BTCST, whereas the indexes CST_SCT3[_RLCSA] are a bit slower than CST_SADA[_RLCSA] because they do not store an explicit tree topology. Note that these operations, in BTCT, make use of the operations fwdsearch and bwdsearch, thereby showing that they are fast although we cannot prove worstcase upper bounds on their time.
Operation lca, which on BTCST involves essentially the primitive minexcess, is costlier, taking around 10 sec in almost all the indexes, including ours. This includes again those where the tree topology is represented using 2.79 bits per node (CST_SADA[_RLCSA]). Thus, although we cannot prove upper bounds on the time of minexcess, it is in practice is as fast as on perfectly balanced structures, where it can be proved to be logarithmictime. The variants CST_SCT3[_RLCSA] also require an operation very similar to minexcess, so they perform almost like CST_SADA[_RLCSA]. For this operation, CST_FULLY is equally fast, owing to the fact that operation lca is a basic primitive in this representation. Only GCST is several times slower than BTCST, taking several tens of sec.
Operation suffixlink involves minexcess and several other operations on the topology, but also the operation on the corresponding CSA. Since the latter is relatively fast, BTCST also takes nearly 10 sec, whereas the additional operations on the topology drive GCST over 100 sec, and CST_FULLY over the millisecond. This time the topology representations that are blind to repetitivess are several times faster than BTCST, taking a few sec, possibly because they take more advantage of the smaller ranges for minexcess involved when choosing random nodes (most nodes have small ranges). The CST_SCT3[_RLCSA] variants also solve this operation with a fast and simple formula.
Finally, operation child is the most expensive one, requiring one application of stringdepth and several of nextsibling and letter, thereby heavily relying on the CSA. BTCSTbin and CST_SCT3[_RLCSA] binary search the children; the others scan them linearly. The indexes using a CSA that adapts to repetitiveness require nearly one millisecond on large alphabets, whereas those using a larger and faster CSA are up to 10 (CST_SCT3) and 100 (CST_SADA) times faster. Our BTCSTbin variant is faster than the base BTCST by 15% on einstein and 18% on kernel, and outperforms the RLCSAbased indexes. On DNA, instead, most of the indexes take nearly 100 sec, except for CST_SADA, which is several times faster; GCSA, which is a few times slower; and CST_FULLY, which stays in the millisecond.
Figure 2 shows the results for the maximal substrings problem. BTCST sharply dominates an important part of the Paretocurve, including the sweet point at 3.5 bps and 200300 sec per symbol. The other structures for repetitive collections take either much more time and slightly less space (GCSA, 1.5–2.5 times slower), or significantly more space and slightly less time (CST_SCT3, 45% more space and around 200 sec). CST_SADA is around 10 times faster, the same as its CSA when solving the dominant operation, child.
6 Future Work
Although we have shown that in practice they perform as well as their classical counterpart [29], an interesting open problem is whether the operations fwdsearch, bwdsearch, and minexcess can be supported in polylogarithmic time on Block Trees. This was possible on perfectly balanced trees [29] and even on balancedgrammar parse trees [28], but the ability of Block Trees to refer to a prefix or a suffix of a block makes this more challenging. We note that the algorithm described by Belazzougui et al. [7] claiming logarithmic time for minexcess does not really solve the operation (as checked with coauthor T. Gagie).
References
 [1] Andrés Abeliuk, Rodrigo Cánovas, and Gonzalo Navarro. Practical compressed suffix trees. Algorithms, 6(2):319–351, 2013.
 [2] Mohamed Ibrahim Abouelhoda, Stefan Kurtz, and Enno Ohlebusch. Replacing suffix trees with enhanced suffix arrays. Journal of Discrete Algorithms, 2(1):53–86, 2004.
 [3] Alberto Apostolico. The myriad virtues of subword trees. In Combinatorial Algorithms on Words, pages 85–96. Springer, 1985.
 [4] Diego Arroyuelo, Francisco Claude, Sebastian Maneth, Veli Mäkinen, Gonzalo Navarro, Kim Nguyn, Jouni Sirén, and Niko Välimäki. Fast inmemory xpath search using compressed indexes. Software Practice and Experience, 45(3):399–434, 2015.
 [5] Djamal Belazzougui and Fabio Cunial. Representing the suffix tree with the CDAWG. In Proc. 28th Annual Symposium on Combinatorial Pattern Matching (CPM), pages 7:1–7:13, 2017.
 [6] Djamal Belazzougui, Fabio Cunial, Travis Gagie, Nicola Prezza, and Mathieu Raffinot. Composite repetitionaware data structures. In Proc. 26th Annual Symposium on Combinatorial Pattern Matching (CPM), pages 26–39, 2015.
 [7] Djamal Belazzougui, Travis Gagie, Pawel Gawrychowski, Juha Kärkkäinen, Alberto Ordónez, Simon J. Puglisi, and Yasuo Tabei. Queries on LZbounded encodings. In Proc. Data Compression Conference (DCC), pages 83–92, 2015.
 [8] David R. Clark and J. Ian Munro. Efficient suffix trees on secondary storage. In Proc. 17th Annual ACMSIAM Symposium on Discrete Algorithms (SODA), pages 383–391, 1996.
 [9] Andrea Farruggia, Travis Gagie, Gonzalo Navarro, Simon J Puglisi, and Jouni Sirén. Relative suffix trees. The Computer Journal, 61(5):773–788, 2018.
 [10] Paolo Ferragina and Roberto Grossi. The string Btree: A new data structure for string search in external memory and its applications. Journal of the ACM, 46(2):236–280, 1999.
 [11] Johannes Fischer, Veli Mäkinen, and Gonzalo Navarro. Faster entropybounded compressed suffix trees. Theoretical Computer Science, 410(51):5354–5364, 2009.
 [12] Travis Gagie, Gonzalo Navarro, and Nicola Prezza. Optimaltime text indexing in BWTruns bounded space. CoRR, 1705.10382, 2017. URL: arxiv.org/abs/1705.10382.
 [13] Simon Gog. Compressed Suffix Trees: Design, Construction, and Applications. PhD thesis, University of Ulm, Germany, 2011.
 [14] Dan Gusfield. Algorithms on Strings, Trees, and Sequences: Computer Science and Computational Biology. Cambridge University Press, 1997.
 [15] WingKai Hon, Rahul Shah, Sharma V. Thankachan, and Jeffrey Scott Vitter. Spaceefficient frameworks for topk string retrieval. Journal of the ACM, 61(2):9:1–9:36, 2014.
 [16] John C. Kieffer and EnHui Yang. Grammarbased codes: a new class of universal lossless source codes. IEEE Transactions on Information Theory, 46(3):737–754, 2000.
 [17] Sebastian Kreft and Gonzalo Navarro. On compressing and indexing repetitive sequences. Theoretical Computer Science, 483:115–133, 2013.
 [18] Stefan Kurtz. Reducing the space requirement of suffix trees. Software Practice and Experience, 29(13):1149–1171, 1999.
 [19] Shanika Kuruppu, Simon J. Puglisi, and Justin Zobel. Relative LempelZiv compression of genomes for largescale storage and retrieval. In Proc. 17th International Conference on String Processing and Information Retrieval (SPIRE), pages 201–206, 2010.
 [20] Jesper Larsson and Alistair Moffat. Offline dictionarybased compression. Proceedings of the IEEE, 88(11):1722–1732, 2000.
 [21] Abraham Lempel and Jacob Ziv. On the complexity of finite sequences. IEEE Transactions on information theory, 22(1):75–81, 1976.
 [22] Veli Mäkinen, Gonzalo Navarro, Jouni Sirén, and Niko Välimäki. Storage and retrieval of highly repetitive sequence collections. Journal of Computational Biology, 17(3):281–308, 2010.
 [23] Udi Manber and Gene Myers. Suffix arrays: A new method for online string searches. SIAM Journal on Computing, 22(5):935–948, 1993.
 [24] Edward M. McCreight. A spaceeconomical suffix tree construction algorithm. Journal of the ACM, 23(2):262–272, 1976.
 [25] Maxim Mozgovoy, Kimmo Fredriksson, Daniel White, Mike Joy, and Erkki Sutinen. Fast plagiarism detection system. In Proc. 12th International Symposium on String Processing and Information Retrieval (SPIRE), pages 267–270, 2005.
 [26] Gonzalo Navarro. Indexing highly repetitive collections. In Proc. 23rd International Workshop on Combinatorial Algorithms (IWOCA), pages 274–279, 2012.
 [27] Gonzalo Navarro and Veli Mäkinen. Compressed fulltext indexes. ACM Computing Surveys, 39(1), 2007.
 [28] Gonzalo Navarro and Alberto Ordóñez. Faster compressed suffix trees for repetitive collections. Journal of Experimental Algorithmics, 21(1):1–8, 2016.
 [29] Gonzalo Navarro and Kunihiko Sadakane. Fully functional static and dynamic succinct trees. ACM Transactions on Algorithms, 10(3):16, 2014.
 [30] Takaaki Nishimoto, Tomohiro I, Shunsuke Inenaga, Hideo Bannai, and Masayuki Takeda. Fully Dynamic Data Structure for LCE Queries in Compressed Space. In Proc. 41st International Symposium on Mathematical Foundations of Computer Science (MFCS), pages 72:1–72:15, 2016.
 [31] Enno Ohlebusch, Johannes Fischer, and Simon Gog. CST++. In Proc. 17th International Conference on String Processing and Information Retrieval (SPIRE), pages 322–333, 2010.
 [32] Alberto Ordóñez. Statistical and repetitionbased compressed data structures. PhD thesis, Universidade da Coruña, 2016.
 [33] Rajeev Raman, Venkatesh Raman, and Srinivasa Rao Satti. Succinct indexable dictionaries with applications to encoding kary trees, prefix sums and multisets. ACM Transactions on Algorithms, 3(4):43, 2007.
 [34] Rajeev Raman and S. Srinivasa Rao. Succinct representations of ordinal trees. In SpaceEfficient Data Structures, Streams, and Algorithms, pages 319–332. Springer, 2013.
 [35] Luís M. S. Russo, Gonzalo Navarro, and Arlindo L. Oliveira. Fully compressed suffix trees. ACM Transactions on Algorithms, 7(4):53:1–53:34, 2011.
 [36] Kunihiko Sadakane. New text indexing functionalities of the compressed suffix arrays. Journal of Algorithms, 48(2):294–313, 2003.
 [37] Kunihiko Sadakane. Compressed suffix trees with full functionality. Theory of Computing Systems, 41(4):589–607, 2007.
 [38] Sarah A. Tishkoff and Kenneth K. Kidd. Implications of biogeography of human populations for ‘race’ and medicine. Nature Genetics, 36:S21–S27, 2004.
 [39] Esko Ukkonen. Online construction of suffix trees. Algorithmica, 14(3):249–260, 1995.
 [40] Peter Weiner. Linear pattern matching algorithms. In Proc. 14th Annual Symposium on Switching and Automata Theory (FOCS), pages 1–11, 1973.
 [41] Dell Zhang and Wee Sun Lee. Extracting key‐substring‐group features for text classification. In Proc. 12th Annual International Conference on Knowledge Discovery and Data Mining (SIGKDD), pages 474–483, 2006.