Abstract 1 Introduction 2 Notation and Preliminary 3 Relative Encoding of 𝐏𝐀 in 𝑶(𝒏) Extra bits 4 Relative Encoding of 𝐏𝐀 in Sublinear Space 5 A Note On Inverse Prefix Array (IPA) Queries 6 Conclusions References

Relative Compressed Reverse Suffix Array

Muhammed Oguzhan Kulekci ORCID Miami University, Oxford, OH, USA Mano Prakash Parthasarathi111Corresponding author ORCID North Carolina State University, Raleigh, NC, USA Rahul Shah ORCID Louisiana State University, Baton Rouge, LA, USA Sharma V. Thankachan ORCID North Carolina State University, Raleigh, NC, USA
Abstract

Suffix trees and suffix arrays are two fundamental data structures in the field of string algorithms. For a string (a.k.a. text or sequence) of length n over an alphabet of size σ, these structures typically require O(nlogn) bits of space. The FM-index provides a compressed representation of the suffix array in nlogσ bits, allowing for efficient queries on both the suffix array and its inverse array in near logarithmic time. In certain applications, such as approximate pattern matching (i.e., with wildcards, mismatches, edits), there is a need to access the suffix array of a text, as well as the suffix array of text’s reverse. Motivated by this, we explore the possibility of encoding the suffix array of the reversed text in a compact form, assuming the availability of the FM-index for the original text. Our first solution is an O(n)-bit (relative) encoding of the suffix array of the reversed text, with the time for decoding an entry being only O(logn) times that of decoding an entry in the text’s suffix array using FM-index. We then demonstrate how to reduce the space to O(n/κ) bits for a parameter κ, while multiplicative factor in time becomes approximately O(κlogn+κ3). We can also support inverse suffix array and longest common extension queries on the reversed text. These results are achieved through some careful and non-trivial application of various succinct data structure techniques.

Keywords and phrases:
String Matching, Text Indexing, Data Structures, Suffix Trees
Funding:
Mano Prakash Parthasarathi: U.S. National Science Foundation (NSF) award CCF-2315822.
Rahul Shah: U.S. NSF awards CCF-2434261 and CCF-2137057
Sharma V. Thankachan: U.S. NSF awards CCF-2316691 and CCF-2315822.
Copyright and License:
[Uncaptioned image] © Muhammed Oguzhan Kulekci, Mano Prakash Parthasarathi, Rahul Shah, and
Sharma V. Thankachan; licensed under Creative Commons License CC-BY 4.0
2012 ACM Subject Classification:
Theory of computation Pattern matching
Editors:
Meena Mahajan, Florin Manea, Annabelle McIver, and Nguyễn Kim Thắng

1 Introduction

The suffix tree data structure [22, 33, 34] has long been a fundamental tool in string processing, enabling tasks such as pattern matching in time linear in the pattern length. However, their use has been limited in practice due to their high memory usage. Suffix arrays [21] were introduced as a more memory-efficient alternative that can perform most of the tasks as suffix trees. Both suffix trees and suffix arrays of a text of length n still require O(nlogn) bits to store, while the text itself only needs nlogσ bits, where σ is the alphabet size. The major breakthrough came in 2000 with the FM-index [7], along with another important development called the compressed suffix array [14]; both of these encode the suffix array in succinct (even compressed) space. Since then, compressed text indexing has continued to be an active area of research, with the FM-index emerging as a foundational component in many bioinformatics tools [18, 20].

In its standard form, the FM-index is essentially the Burrows-Wheeler Transform (BWT) of the text, stored in a data structure that supports rank queries (typically using a wavelet tree [13, 24]), along with a sampled subset of suffix array values. The wavelet tree part uses nlogσ+o(n)+O(σlogn) bits, and supports rank queries in time twt=O(1+logσ/loglogn). For a parameter τ, the number of sampled suffix array values is n/τ and its storage takes O((n/τ)logn) bits. To keep this extra space within o(n) bits, τ is typically chosen as ω(logn). With this, the suffix array (SA) and inverse suffix array (ISA) queries can be supported in time tsa=O(τtwt). With additional o(n) bits, we can obtain an encoding of the suffix tree itself that supports various queries in time close to O(tsa) [31, 30]. Recent results have revealed the possibility of achieving even more space-efficient encodings of suffix arrays/trees when the data is highly repetitive [10, 16, 26, 27].

We investigate the problem of compactly encoding the suffix array of the reverse of a text, assuming access to the text’s FM-index in its standard form (i.e., the Burrows–Wheeler Transform represented as a wavelet tree together with a sampled suffix array). While this is a natural question in its own right, solutions to it have important applications in text indexing for approximate string matching. A common technique for supporting pattern matching with mismatches or “don’t care” characters involves maintaining both the suffix array or tree of the text and that of its reverse, along with a range query data structure built from specific points derived from these structures [2]. For a comprehensive survey of various results based on this technique, we refer the reader to Lewenstein [19]. See also the solutions for contextual pattern matching [1, 25], bidirectional indexes [17], and affix arrays [32] for further applications. Reducing the memory footprint of the data structures associated with the reversed text can significantly reduce the overall space usage in such applications. We highlight that our problem aligns with the broader theme of relative text indexing, such as relative suffix trees [6] and relative FM-indexes [3], where the goal is to encode a data structure over a dataset by leveraging the availability of the same structure over a closely related dataset. The fact that, in our case, one text is essentially the reverse of the other introduces significant structure to the problem.

This problem was introduced by Ganguly et al. [12], who presented an algorithm to decode the suffix array values of the reversed text directly from the text’s FM-index, without requiring any additional data structures. However, it was heuristic in nature. Specifically, its query time is proportional to the length of the shortest unique substring of the text ending at the value returned, which can be Θ(n) in the worst case. Therefore, our goal is to support suffix array (and inverse suffix array) queries on the reversed text in time close to that of suffix array queries on the original text. We present our results next.

Our Results

Let 𝖳[1..n] denote the text to be indexed, where the characters belong to an integer alphabet of size σ. We can represent the text’s Burrows–Wheeler Transform (BWT) as a (multiary) wavelet tree in nlogσ+o(n)+O(σlogn) bits of space, supporting basic wavelet tree operations (rank, select, access) in time twt=O(1+logσ/loglogn) and range quantile queries in time tquantile=O(logσ). By maintaining some auxiliary structures of space O(n/κ) bits for an integer parameter κ1, we can support suffix array (SA) and inverse suffix array (ISA) queries in time tsa=O(twtκlogn). We obtain the following results.

  1. 1.

    Our first result is that suffix array queries on the reversed text can be supported in time tpa=tsaO(logn) with an auxiliary structure of O(n) bits of space.

  2. 2.

    We then generalize the above result by introducing a parameter κ, achieving a space usage of O(n/κ) bits and a query time tpa=tsaO((κlogn+κ3logκ)log2κ).

We also show how to support inverse suffix array queries on the reversed text within the same space and in time O(tpalognloglogn), which is based on a simple method for answering longest common extension queries on the reversed text. As shown in [31, 30], we can use o(n) extra bits to obtain a succinct encoding of the suffix tree of the reversed text that supports various standard operations efficiently.

2 Notation and Preliminary

Throughout this paper, let 𝖳=𝖳[1..n] denotes the text to be indexed and Σ denotes the alphabet set. The i-th character of 𝖳 is denoted by 𝖳[i] and the substring 𝖳[i]𝖳[i+1]𝖳[j1]𝖳[j] is denoted by 𝖳[i..j], where 1ijn and denotes concatenation. The string 𝖳[i..j] is empty if i>j. We also use 𝖳[i..j) for 𝖳[i..j1] and 𝖳(i..j] for 𝖳[i+1..j]. A substring 𝖳[i..j] is called a suffix if j=n and a prefix if i=1; also 𝖳[1..j] is a proper prefix if jn and 𝖳[i..n] is a proper suffix if i1. For any string S=S[1..s], we denote its reverse S[s]S[s1]S[2]S[1] by S. Without loss of generality, assume that the characters in our alphabet set Σ corresponds to integers in the range [0,σ), where σ=|Σ|. Also, let $ denote the first character in lexicographic order (equivalently, 0) and 𝖳[i]=$ iff i=n, ensuring that no suffix is a prefix of another suffix.

We call a data structure auxiliary if it is used to support operations assuming some related structures are available; otherwise, it is considered standalone. We assume the standard word RAM model of computation with word size Ω(logn) bits.

2.1 Succinct Data Structure Toolkit

2.1.1 Rank/select Queries, Indexible Dictionaries and Wavelet Trees

Let S[1..n]Σn, where Σ={0,1,2,,σ1}. We define three basic operations.

  • 𝖺𝖼𝖼𝖾𝗌𝗌S(i) is S[i].

  • 𝗋𝖺𝗇𝗄S(i,α), where αΣ, is the number of α’s in S[1..i].

  • 𝗌𝖾𝗅𝖾𝖼𝗍S(j,α) is the position of j-th occurrence of α in S.

When Σ={0,1}, we can maintain S in n+o(n) bits and support all three operations in O(1) time [28]. Another representation, called indexible dictionary takes mlog(n/m)+O(m) bits, where m is the number of 1’s in S [29]. It supports the above operations in O(loglogn) time. Interestingly, it can support 𝗌𝖾𝗅𝖾𝖼𝗍S(j,1) in O(1) time, and also 𝗋𝖺𝗇𝗄S(i,1) when S[i]=1 (referred to as partial rank) in O(1) time. We will use this result extensively.

For a general alphabet Σ, we have the wavelet tree data structure that uses nlogσ+o(n)+O(σlogn) bits of space and supports all three basic operations in O(logσ) time [13]. It can also support range quantile queries in O(logσ) time [11], where the goal is to find the k-th smallest element in S[x..y], given the tuple (x,y,k). Multiary wavelet trees offer further improvements, reducing the time complexity of the three basic operations to O(1+logσ/loglogn) [8]; however, the time for range quantile queries remains O(logσ). Also note that the o(n) term in space can be made O(n/logcn) for any constant c using efficient bit string representations [28]. We refer to Navarro’s survey for further reading on this topic [24]. Moving forward, we use twt to denote the time complexity of the basic operations, and tquantile to represent the time complexity of a range quantile query.

2.1.2 Range Minimum Query (RMQ)

Let A[1..n] be an array of numbers. A range minimum query RMQA(x,y) returns the position k[x,y], where A[k] is the smallest element in A[x..y]. By maintaining a standalone data structure of space 2n+o(n) bits, we can support RMQ in O(1) time [9].

2.1.3 Succinct Representation of Ordinal Trees

The topology of an ordinal tree of n nodes can be represented in 2n+o(n) bits and support the following operations in O(1) time [15, 23]. We specify a node by its preorder rank.

  • 𝗉𝖺𝗋𝖾𝗇𝗍(u) returns the parent of node u.

  • LCA(u,v) returns the lowest (farthest from the root) common ancestor of nodes u and v.

  • 𝗌𝗂𝗓𝖾(u) returns the number of leaves in the subtree of node u.

  • 0pt(u) returns the number of ancestors of node u.

  • 𝗅𝖾𝗏𝖾𝗅_𝖺𝗇𝖼𝖾𝗌𝗍𝗈𝗋(u,d) returns the ancestor of node u at 0pt d.

  • range of leaves [a,b] of any given node u, where a-th (resp., b-th) leaf in the tree represents the leftmost (resp., rightmost) leaf in the subtree of u.

2.2 Suffix Tree, Suffix Array and Longest Common Prefix (LCP) Array

The suffix of 𝖳 starting at position i is 𝖳[i..n]. The circular suffix of 𝖳, starting at position i is 𝖳[i..n]𝖳[1..i)=𝖳[i]𝖳[i+1]𝖳[n]𝖳[1]𝖳[2]𝖳[i1].

We define the suffix tree of text 𝖳 as a compacted trie of all circular suffixes of 𝖳. This is equivalent to the standard definition (i.e., without the term “circular”), since 𝖳[i]=$ iff i=n. It consists of n leaves and less than n internal nodes. The edges are (implicitly) labeled with substrings of 𝖳. We use str(u) to denote the concatenation of edge labeled on the path from root to node u. Also, let strlen(u) be the length of str(u) (called string depth). The i-th leftmost leaf in the suffix tree is denoted by i. The suffix array and the inverse suffix array of 𝖳 are the arrays SA[1..n] and ISA[1..n], respectively, where SA[j]=i and ISA[i]=j iff the j-th smallest circular suffix in lexicographic order is 𝖳[i..n]𝖳[1..i). Equivalently, str(j)=𝖳[i..n]𝖳[1..i).

For a string P, the locus node u (if it exists) is the node closest to root such that P is a prefix of str(u) and the range of leaves of u is called the suffix range of P, denoted by [𝗌𝗉(P),𝖾𝗉(P)]. The set {SA[k]k[𝗌𝗉(P),𝖾𝗉(P)]} denotes the occurrences of P in 𝖳. The longest common extension, denoted by LCE(i,j) is the length of the longest common prefix of the suffixes of 𝖳 starting positions i and j, which is the same as strlen(u), where u is the lowest common ancestor (LCA) of leaves ISA[i] and ISA[j].

The longest common prefix array LCP[1..n) is an array where LCP[i]=LCE(SA[i],SA[i+1]). Therefore, LCE(i,j) is the smallest element in LCP[x..y), where x=min{ISA[i],ISA[j]} and y=max{ISA[i],ISA[j]}. The permuted longest common prefix array pLCP[1..n) is defined as pLCP[j]=LCP[ISA[j]]=LCE(j,SA[ISA[j]+1]). Note that the argument of LCP[] is a leaf position in the suffix tree, whereas that of pLCP[] is a position in the text. We will later see that pLCP array can be encoded in 2n+o(n) bits. See Table 1 for an example.

Table 1: SA,ISA,LCP,pLCP,BWT,PA,IPA,LCS, and pLCS values for 𝖳[1..12]=mississippi$.
i 1 2 3 4 5 6 7 8 9 10 11 12
𝖳[1..12] m i s s i s s i p p i $
SA[1..12] 12 11 8 5 2 1 10 9 7 4 6 3
ISA[1..12] 6 5 12 10 4 11 9 3 8 7 2 1
LCP[1..12] 0 1 1 4 0 0 1 0 2 1 3 -
pLCP[1..12] 0 0 - 1 4 3 2 1 0 1 1 0
BWT[1..12] i p s s m $ p i s s i i
PA[1..12] 12 2 11 5 8 1 9 10 3 6 4 7
IPA[1..12] 6 2 9 11 4 10 12 5 7 8 3 1
LCS[1..12] 0 1 1 4 0 0 1 0 2 1 2 -
pLCS[1..12] 0 1 2 2 4 1 - 0 1 0 1 0

2.3 Prefix Tree, Prefix Array and Longest Common Suffix (LCS) Array

We now introduce the prefix tree and prefix array of the text, which are equivalent to the suffix tree and suffix array of the reversed text. These alternative definitions are introduced purely for notational convenience.

The prefix of 𝖳 ending at position i is 𝖳[1..i]. The circular prefix of 𝖳 ending at i is 𝖳(i..n]𝖳[1..i] and its reverse is 𝖳[i]𝖳[i1]𝖳[1]𝖳[n]𝖳[n1]𝖳[i+1]. The prefix tree of 𝖳 is a compacted trie of the reverse of all circular prefixes of 𝖳. It will have n leaves and less than n internal nodes, and the edges will be labeled as in the suffix tree, but with substrings of 𝖳. We shall reuse the notations i, str() and strlen() as before when the context is clear. We define prefix array PA[1..n] and inverse prefix array IPA[1..n] of 𝖳 as PA[j]=i and IPA[i]=j iff str(j) is the “reverse” of the circular prefix of 𝖳 ending at i. Define LCE(i,j) as the length of the longest common suffix of the prefixes of 𝖳 ending at positions i and j. Therefore, LCE(i,j)=strlen(u), where u is the LCA of leaves IPA[i] and IPA[j] in the prefix tree. The longest common suffix (LCS) array LCS[1..n) is an array where LCS[i]=LCE(PA[i],PA[i+1]) and the permuted longest common suffix (pLCS) array is such that pLCS[j]=LCS[IPA[j]]=LCE(j,PA[IPA[j]+1]). See Table 1 for an example.

2.4 The FM-index

The FM-index [7] is a compressed representation of the suffix array SA, based on the text’s BWT [5]. It is a self-index, meaning it does not require access to the original text in order to function. The BWT of 𝖳, denoted by BWT[1..n] is a permutation of characters in 𝖳, where BWT[i] denotes the last character of i-th lexicographically smallest circular suffix of 𝖳. In other words, BWT[i]=𝖳[SA[i]1] if SA[i]1 and is $ otherwise. See Table 1 for an example.

The Last-to-Front operation is defined as LF[i]=ISA[SA[i]1] if SA[i]1 and is 1 otherwise. Moreover, LF[i]=Count(c)+𝗋𝖺𝗇𝗄BWT(i,c), where c=BWT[i] and Count(c)=|{jBWT[j]<c}|. By maintaining BWT as a wavelet tree, along with Count(c) for all cΣ, in O(σlogn) bits, we can support LF[] operation in O(twt) time.

We next focus on SA[] and ISA[] operations. Let LFk[i]=LF[LFk1[i]] if k>1 and is LF[i] if k=1, then SA[LFk[i]]=SA[i]k. For a parameter τ, explicitly store the pairs (i,SA[i]) with SA[i] being a multiple of τ; space is O((n/τ)logn) bits. To decode SA[i], where (i,SA[i]) is not stored, we compute LF[i],LF2[i],LF3[i], until we find an LFk[i], where (LFk[i],SA[LFk[i]]) is stored, and return SA[i]=k+SA[LFk[i]]. To decode ISA[j], where (ISA[j],j) is not stored, we find j=τj/τ and i=ISA[j] (note that (i,j) is stored) and return LFk[i], where k=jj. In both cases, the sampling scheme guarantees kτ and the resulting time complexity, denoted by tsa, is ktwt=O(τtwt).

We can also extract any substring 𝖳[j..j+r) in time O(tsa+rtwt) as follows: find x=ISA[j+r], LF[x],LF2[x],LF3[x],,LFr1[x] successively and output the string BWT[LFr1[x]]BWT[LF[x]]BWT[x].

Lemma 1.

Suppose the text’s BWT is available as a wavelet tree supporting basic operations (i.e., 𝗋𝖺𝗇𝗄,𝗌𝖾𝗅𝖾𝖼𝗍 and 𝖺𝖼𝖼𝖾𝗌𝗌) in twt time, then with O((n/τ)logn) extra bits, we can support SA/ISA queries in time tsa=O(τtwt) and extract any substring 𝖳[j..j+r) in time O(tsa+rtwt). Here, τ is a parameter.

2.5 Longest Common Extension (LCE) and Related Queries

The following two results, along with the succinct representation of the suffix tree topology as in Section 2.1.3, gives the compressed representation of the suffix tree. We present sketches of the proofs for these known results, since we will be adapting them in subsequent sections.

Lemma 2 (Sadakane [31]).

The pLCP array can be encoded in 2n+o(n) bits, such that pLCP[j] for any position j in the text can be retrieved in O(1) time.

Proof.

Key observation is that pLCP[j+1]pLCP[j]1 for all j. To prove this, note that it is trivially true when pLCP[j]=0. Otherwise, let SA[ISA[j]+1]=j. Then ISA[j+1]<ISA[j+1], therefore pLCP[j+1] is atleast LCE(j+1,j+1)=pLCP[j]1. By rearranging the inequality and by adding j on both sides, we get j+pLCP[j](j+1)+pLCP[j+1]. This means, the function f(j)=j+pLCP[j] is non-decreasing. Moreover f(j)[1,n], because pLCP[n]=0 and hence f(n)=n. Therefore, we can store the function f() as a bit string B=0f(1)10f(2)f(1)10f(3)f(2)1 with O(1) time rank/select support in 2n+o(n) bits. To decode pLCP[j], we first find p=𝗌𝖾𝗅𝖾𝖼𝗍B(j,1), the position of j-th 1 in B in O(1) time. Then we have f(j)=pj, the number of 0’s until p, and pLCP[j]=f(j)j.

Lemma 3 (LCE Queries).

By maintaining an auxiliary structure of space O(n) bits along with the FM-index, we can answer LCE(,) queries in O(tsa) time.

Proof.

Maintain the encoding of the pLCP array described in Lemma 2 and an RMQ data structure over the LCP array, but without explicitly storing the LCP array itself. To compute LCE(i,j), find i=ISA[i] and j=ISA[j]. Without loss of generality, assume that i<j. Then find k[i,j), where LCP[k] is the smallest in LCP[i..j) using an RMQ in O(1) time. Then compute k=SA[k] and return pLCP[k]. Total time is O(tsa).

We next present an additional result that will be used later.

Lemma 4 (LCE Queries).

Let D be an integer parameter. By maintaining an auxiliary structure of space O((n/D)logn) bits, we can compute LCE(i,j) for any given i,j via O(logD) number of LCE queries.

Proof.

For any given text of length n, there exists a standalone data structure of space O(n/D) words, equivalently O((n/D)logn) bits, that in constant time either correctly computes LCE(i,j) or determines that LCE(i,j)D2 for any given i,j [4]. We maintain this data structure over the reversed text, which allows us to correctly compute LCE(i,j) or to determine that LCE(i,j)D2. When LCE(i,j)D2, we exploit the following observation: LCE(i,j)t if and only if LCE(it+1,jt+1)t, and binary search on t[0,D2]. This will give us LCE(i,j) in O(logD) number of LCE queries.

3 Relative Encoding of 𝐏𝐀 in 𝑶(𝒏) Extra bits

The prefix tree/array of 𝖳 corresponds to the suffix tree/array of T. Specifically, the i-th entry in the suffix array of 𝖳 is (nPA[i]+1). Therefore, it suffices to design an encoding of the prefix array.

3.1 Basic Components

Let π0 be an integer parameter (positive or negative). We now introduce arrays LCPπ and pLCPπ, which are generalizations of LCP array and pLCP array as follows:

LCPπ[i]=LCE(SA[i],SA[i+π]), where i+πn
pLCPπ[j]=LCPπ[ISA[j]]=LCE(j,SA[ISA[j]+π]), where ISA[j]+πn

The encoding technique by Sadakane (see Lemma 2) extends to the pLCPπ array as follows.

Lemma 5.

Let π0 be an integer parameter (positive or negative). We can encode pLCPπ in O(n) bits, and return pLCPπ[j] for any text position j in O(1) time.

Proof.

We first prove that pLCPπ[j+1]pLCPπ[j]1 for all j. Clearly, the inequality holds when pLCPπ[j]=0. Otherwise, let SA[ISA[j]+π]=j and SA[ISA[j+1]+π]=j′′. Then we can see that ISA[j+1]<ISA[j′′]ISA[j+1] if π is positive and ISA[j+1]>ISA[j′′]ISA[j+1] if π is negative. In both cases, pLCPπ[j+1]=LCE(j+1,j′′)LCE(j+1,j+1)LCE(j,j)1=pLCPπ[j]1. By rearranging the inequality as in the proof of Lemma 2, we obtain a non-decreasing function fπ(j)=j+pLCPπ[j]𝒪(n). We store fπ as bit string B=0fπ(1)10fπ(2)fπ(1)10fπ(3)fπ(2)1 with O(1) time rank/select support in O(n) bits. Then pLCPπ[j] is given by fπ(j)j=(𝗌𝖾𝗅𝖾𝖼𝗍B(j,1)j)j.

Lemma 6 (Sampled pLCPπ).

Let π0 be an integer parameter (positive or negative) and S[n] be a set of size s. There exists a standalone structure of space O(slog(n/s)) bits that returns pLCPπ[j] for any given jS in O(1) time.

Proof.

We modify the proof in Lemma 5 as follows. Define a bit string C[1..n], where C[j]=1 iff jS. Define another bit string B=0fπ(s1)10fπ(s2)fπ(s1)10fπ(s3)fπ(s2)1, where si=𝗌𝖾𝗅𝖾𝖼𝗍C(i,1). Maintain C and B as indexible dictionaries in O(slog(n/s)) bits with efficient rank/select support. Given any jS, we find p=𝗋𝖺𝗇𝗄C(j,1) in O(1) time using a partial-rank query. Then find fπ(j)=𝗌𝖾𝗅𝖾𝖼𝗍B(p,1)p and report pLCPπ[j]=fπ(j)j.

The Longest common suffix (LCS) array and the permuted longest common suffix (pLCS) array can also be generalized as follows.

LCSπ[i]=LCE(PA[i],PA[i+π]), where i+πn
pLCSπ[j]=LCSπ[IPA[j]]=LCE(j,PA[IPA[j]+π]), where IPA[j]+πn

Since LCSπ and pLCSπ are equivalent to LCPπ and pLCPπ of the reverse of 𝖳, the following result is an immediate corollary of Lemma 6.

Corollary 7 (Sampled pLCSπ).

Let π0 be an integer parameter (positive or negative) and S[n] be a set of size s. There exists a standalone structure of space O(slog(n/s)) bits that returns pLCSπ[j] for any given jS in O(1) time.

3.2 The Data Structure

Assuming the availability of an FM-index of 𝖳 in its standard from as described in Section 2.4, we now present components in the relative encoding of the prefix array PA of 𝖳. For h[1,logn], define Δh as (log(h)n)2 round to the integer which is the next power of 2. i.e., Δh=22log(h+1)n. Here log(h)n=logn if h=1 and is log(log(h1)n) if h>1, and logn is the smallest h where log(h)n1. Therefore, Δlogn=1. Also let Δ0=n+1. We denote the predecessor and successor of i that are multiples of Δh by ih=Δhi/Δh and ih′′=Δhi/Δh, respectively. Therefore, when Δh divides i, we have ih=ih′′=i.

3.2.1 Technical Overview

If i is divisible by Δ1, we explicitly store PA[i] using O(logn) bits. If i is divisible by Δ2, but not by Δ1, we encode PA[i] relative to either PA[i1] or PA[i1′′] (depending on the case, to be described) using logΔ1 bits. Similarly, if i is divisible by Δ3, but not by Δ2, we encode PA[i] relative to either PA[i2] or PA[i2′′] using logΔ2 bits. More generally, we encode PA[i] relative to either PA[if1] or PA[if1′′] using logΔf1 bits, where flogn is the smallest integer such that i is divisible by Δf. The function Δh is chosen carefully such that the total space usage is bounded by a sum that converges to O(n) bits. The algorithm for decoding PA[i] works as follows: for h=1,2,, decode either ih or ih′′ (depending on the case) and terminate at h=f where if=if′′=i, thereby yielding PA[i]. We present the details next.

3.2.2 Components of the Data Structure

For each h[1,logn], maintain arrays PAh[1,n/Δh] as described below.

  • If i is divisible by Δ1, we store PA[i] explicitly in PA1 as PA1[i/Δ1]=PA[i].

  • If i is not divisible by Δ1, we find the smallest integer f where i is divisible by Δf; such an f[2,logn] exists since Δlogn=1. Then store a value θi in PAf as PAf[i/Δf]=θi. The value θi is determined by certain nodes in the prefix tree, which in turn are based on if1 and if1′′ as illustrated in Figure 1. Let u=LCA(if1,i) and u′′=LCA(i,if1′′), and u be the lowest node among u and u′′. Also let Q=str(u) and node w be the child of u on the path to i and β be the leading character on the edge from u to w. This means that a substring of 𝖳 matching βQ ends at position PA[i], and therefore starts at PA[i]|Q|. Consequently, SA[𝗌𝗉(βQ)..𝖾𝗉(βQ)] must contain the entry PA[i]|Q|. Then θi is defined as the offset within the suffix range of βQ, Specifically,

    SA[𝗌𝗉(βQ)+θi]=PA[i]|Q|.

    Finally, 0θi𝖾𝗉(βQ)𝗌𝗉(βQ)+1=𝗌𝗂𝗓𝖾(w)<Δf1; therefore, we can store θi using logΔf1=O(log(f)n) bits.

Figure 1: Here u=u. The dashed lines denote the range leaves under the respective nodes.

While decoding PA[i], the answer is readily available from PA1 if i is a multiple of Δ1. Otherwise, we utilize the relationship PA[i]=SA[𝗌𝗉(βQ)+θi]+|Q|, where θi is retrieved from PAf; however, |Q| and 𝗌𝗉(βQ) are not explicitly stored. To find them, we maintain the following auxiliary structures.

  • The succinct encodings of the topologies of both the suffix tree and the prefix tree.

  • For each h[1,logn], we maintain a collection of structures as in Corollary 7. Precisely, for a fixed h, we assign S={jISA[j] is divisible by Δh1} and make a separate structure for each non-zero value of π{Δh1,Δh1+Δh,,Δh1Δh,Δh1}. Note that for a fixed h, |S|=n/Δh1 and the number of structures is 2Δh1/Δh.

This completes the description of our data structure.

3.2.3 Space Complexity Analysis

The length of PAh is n/Δh and each entry in PAh is encoded in logΔh1=O(log(h)n) bits. Therefore, space is n/ΔhlogΔh1=O(n/log(h)n) bits for a fixed h and h=1lognnlog(h)n=O(n) bits for all h[1,logn] combined. The tree topologies takes O(n) bits. The space (in bits) required by all versions of the structures in Corollary 7 can also be bounded by O(n), as shown below, resulting in an overall space complexity of O(n) bits.

h=1logn2Δh1ΔhnΔh1logΔh1=2h=1lognnlog(h)n=O(n).

3.3 Algorithm for Decoding 𝐏𝐀[𝒊]

At first, compute f[1,logn], the smallest integer such that Δf divides i. Then, identify nodes u=LCA(if1,i) and u′′=LCA(i,if1′′) in the prefix tree, and let u be the lowest node among them. This step takes only O(1) time using tree encodings. Let Q=str(u) and β be the leading character on the edge from u to its child w on the path to i. Our next goal is to compute |Q| and the suffix range [𝗌𝗉(βQ),𝖾𝗉(βQ)] of βQ, then return PA[i]=SA[𝗌𝗉(βQ)+θi]+|Q|. There are two cases.

  1. 1.

    Case u=u (i.e., u=u=u′′ or u=uu′′): In this case, PA[i] is encoded relative to PA[if1]. We start with an assumption that r=PA[if1] has already been computed. Observe that |Q|=strlen(u)=LCE(PA[if1],PA[i])=pLCSπ[r], where π=iif1. Note also that rS={jISA[j] is divisible by Δf1} and π{Δh,2Δh,,Δh1}. Recall that we already maintain the data structure of Corollary 7 for this specific choice of S and π; querying it yields pLCSπ[r], and thus |Q|, in O(1) time. We then infer Q=𝖳[r|Q|+1..r] and invoke the steps below.

    1. (a)

      Find the suffix range [𝗌𝗉(Q),𝖾𝗉(Q)] of Q: Find the leaf g in the suffix tree, where g=ISA[r|Q|+1] (this leaf corresponds to the suffix 𝖳[r|Q|+1..]), then find its ancestor v such that 𝗌𝗂𝗓𝖾(v)=𝗌𝗂𝗓𝖾(u), and return the range of leaves of v as the answer. Note that v exists and it can be computed via binary search, specifically O(logn) number of 𝗅𝖾𝗏𝖾𝗅_𝖺𝗇𝖼𝖾𝗌𝗍𝗈𝗋 queries. The first step requires an ISA query (in time tsa), the second step takes O(logn) time and the third step takes O(1) time.

    2. (b)

      Find β: Let [a,b] be the range of leaves of u (in the prefix tree) and let k=ia+1. Then, i will be the k-th leftmost leaf in the subtree of u, which implies β will be the k-th smallest character in BWT[𝗌𝗉(Q)..𝖾𝗉(Q)]. So, perform a range quantile query in time tquantile and find β.

    3. (c)

      Find the suffix range [𝗌𝗉(βQ),𝖾𝗉(βQ)] of βQ: Let x (resp., y) be the first (resp., last) occurrence of β in BWT[𝗌𝗉(Q)..𝖾𝗉(Q)]. Then, [LF[x],LF[y]] will be the suffix range of βQ which can be answered in O(twt) time.

    4. (d)

      Compute PA[i]=SA[𝗌𝗉(βQ)+θi]+|Q| and return it. Note that θi is stored explicitly, and hence this step takes 𝒪(tsa) time.

    Total time is O(tsa+logn+tquantile+twt).

  2. 2.

    Case uu (i.e, u=u′′u): In this case, PA[i] is encoded relative to PA[if1′′]. As before, we assume that r=PA[if1′′] has already been computed. Observe that |Q|=strlen(u)=LCE(PA[if1′′],PA[i])=pLCSπ[r], where π=iif1′′. Note also that rS={jISA[j] is divisible by Δf1} and π{Δh,2Δh,,Δh1}. Recall that we already maintain the structure of Corollary 7 for this specific choice of S and π; querying it yields |Q|=pLCSπ[r] in O(1) time. We then infer Q=𝖳[r|Q|+1..r], invoke the same steps as before and obtain PA[i] in O(tsa+logn+tquantile+twt) time.

In summary, decoding PA[i] reduces to decoding either PA[if1] or PA[if1′′] (depending on the case) in O(tsa+logn+tquantile+twt) time, which recursively reduces to decoding PA[if2] or PA[if2′′], and so forth, until reaching PA[i1] or PA[i1′′], which are explicitly stored. The overall time is O((tsa+logn+tquantile+twt)f)O((τtwt+logn+tquantile)logn).

Theorem 8.

Let the Burrows-Wheeler Transform (BWT) of a text 𝖳[1..n] be maintained in a data structure that supports basic queries (𝗋𝖺𝗇𝗄, 𝗌𝖾𝗅𝖾𝖼𝗍, and 𝖺𝖼𝖼𝖾𝗌𝗌) in twt time, and range quantile queries in tquantile time. Then, for any parameter τ1, we can maintain a sampled subset of n/τ suffix array values using O((n/τ)logn) bits of additional space, and support suffix array (SA), inverse suffix array (ISA), and longest common extension (LCE) queries in time tsa=O(τtwt).

By maintaining an auxiliary structure of space O(n) bits, we can support prefix array (PA) queries in time tpa=O((τtwt+tquantile+logn)logn).

By employing the multiary wavelet tree, where twt=O(1+logσ/loglogn) and tquantile=O(logσ) [8], and setting τ=logn, we obtain the following result.

Corollary 9.

For any text 𝖳[1..n] over an alphabet of size σ, we can represent its Burrows–Wheeler Transform using a (multiary) wavelet tree in nlogσ+o(n)+O(σlogn) bits of space, supporting basic wavelet tree operations (rank, select, access) in time O(1+logσ/loglogn) and range quantile queries in time O(logσ). By maintaining some auxiliary structures of space O(n) bits, we can support suffix array (SA), inverse suffix array (ISA), and longest common extension (LCE) queries in time tsa=O((1+logσ/loglogn)logn), and prefix array (PA) queries in time tpa=O(tsalogn).

4 Relative Encoding of 𝐏𝐀 in Sublinear Space

In this section, we present an O((n/δ)logδ)-bit (relative) encoding of PA, where δ>0 is an integer parameter. Without loss of generality, we assume that δ is a power of 2.

4.1 Basic Components

We now present several useful results.

Lemma 10 (Approximate pLCPπ in small space).

Let π0 be an integer parameter (positive or negative). There exists a standalone structure of space O((n/δ)logδ) bits that returns pLCPπ[j] in O(logδ) time, where pLCPπ[j]δpLCPπ[j]pLCPπ[j].

Proof.

We obtain this result by modifying Lemma 6. The idea is to select a set S{1,2,3,,n} of sampled positions and store fπ(j) values of only those jS. The set S is constructed as follows. Initially S={1}. Then for j=2 to n, we include j in S iff j is a multiple of δ or fπ(j)fπ(j)>δ, where j<j is the largest value in S currently. The size of S is Θ(n/δ) since fπ()2n and is non-decreasing. For a query j, we return fπ(j)j as pLCPπ[j], where j is the predecessor of j in S. The correctness follows from that fact that fπ(j)fπ(j)=pLCPπ[j]+jfπ(j)+δ and jjj+δ, which implies pLCPπ[j]δfπ(j)jpLCPπ[j].

We now present the implementation details. Define a bit string C corresponding to S, where C[j]=1 iff jS. Define another bit string B=0fπ(s1)10fπ(s2)fπ(s1)10fπ(s3)fπ(s2)1, where si=𝗌𝖾𝗅𝖾𝖼𝗍C(i,1). We maintain both C and B as indexible dictionaries with efficient rank/select support. The space required is O((n/δ)logδ) bits since both B and C are of size O(n) with Θ(n/δ) number of 1’s. To recover an approximate value of pLCPπ[j], we first find k=δj/δS and r=𝗋𝖺𝗇𝗄C(k,1) using a partial rank query in O(1) time. Then j, the predecessor of j in S is 𝗌𝖾𝗅𝖾𝖼𝗍C(t,1) for the t[r,r+δ], where 𝗌𝖾𝗅𝖾𝖼𝗍C(t,1)j<𝗌𝖾𝗅𝖾𝖼𝗍C(t+1,1), which can be computed in O(logδ) time via binary search. Finally, get fπ(j)=𝗌𝖾𝗅𝖾𝖼𝗍B(t,1)t and return pLCPπ[j]=fπ(j)j.

Lemma 11 (Approximate pLCSπ in small space).

Let π0 be an integer parameter (positive or negative). There exists a standalone structure of space O((n/δ)logδ) bits that returns pLCSπ[j] in O(logδ) time, where pLCSπ[j]δpLCSπ[j]pLCSπ[j].

Proof.

Follows from Lemma 10, because pLCSπ is equivalent to pLCPπ of text’s reverse.

Lemma 12 (Exact pLCP in small space).

By maintaining an O((n/δ)logδ)-bit auxiliary structure with the FM-index, we can return pLCP[j] for any j in O(δtwt+tsa) time.

Proof.

Maintain the structure in Lemma 10 for π=1. Let j=SA[ISA[j]+1], then pLCP[j]=LCE(j,j). Find h=pLCPπ[j] first. Now we know LCE(j,j)=h+LCE(j+h,j+h) and LCE(j+h,j+h)δ. Therefore, simply extract the substrings 𝖳[j+h..j+h+δ) and 𝖳[j+h..j+h+δ) and compute LCE(j+h,j+h) naively.

Lemma 13 (Exact LCE(,) in small space).

By maintaining an O((n/δ)logδ)-bit auxiliary structure with FM-index, we can return LCE(i,j) in time tLCE=O((δtwt+tsa)δ) time.

Proof.

We divide the LCP array into blocks of size δ. i.e., LCP[1..δ],LCP[δ+1..2δ],LCP[2δ+1..3δ], etc. Also let LCP[x] be the smallest element in x-th block. Maintain a range minimum query structure over LCP array (space required is 2n/δ+o(n/δ) bits). Besides this, we will use the result in Lemma 12. When a query (i,j) comes, we compute LCE(i,j) as follows. Let i=ISA[i] and j=ISA[j]. Without loss of generality, lets assume that i<j. To efficiently find our answer mink[i,j)LCP[k], we invoke the following procedure.

  • If ji<3δ, we extract LCP[k]=pLCP[SA[k]]k[i,j) and return the minimum.

  • Otherwise, we extract LCP[k]=pLCP[SA[k]]k[i,i+δ)(δk,δk+δ](jδ,j), where k=RMQLCP(1+i/δ,j/δ) and return the minimum.

In both cases, we are required to make two ISA queries, O(δ) number of SA queries, followed by O(δ) number of pLCP[] queries, which makes the total time O((δtwt+tsa)δ).

4.2 The Data Structure

4.2.1 Technical Overview

We categorize a query PA[i] as Type-A if i is divisible by δ (recall that δ is a power of 2), and as Type-B otherwise. These two types are handled separately. For Type-A queries, we modify our O(n)-bit solution in a relatively simple manner. Instead of maintaining structures up to logn levels, we stop at an earlier stage, which suffices to handle such queries and also helps bound the space. Furthermore, we substitute the standard suffix and prefix tree topologies with their sampled counterparts. However, for Type-B queries, we first decode the PA values of the predecessor and the successor which are multiples of δ (i.e., i=δi/δ and i′′=δi/δ), by treating them as Type-A queries. Then, we decode all the entries in PA[i..i′′], first in an unsorted manner; afterward, we sort them to obtain PA[i]. We accomplish this by splitting this range into the minimal number of subranges such that each subrange corresponds to a subtree of the prefix tree, and processing each subrange carefully. This part is more technical.

4.2.2 Handling Type-A Queries

Observe that our O(n)-bit encoding of PA composed of two tree topologies and several components corresponding to each h[1,logn]. However, only the components corresponding to h[1,f] are utilized when decoding PA[i], where f is the smallest integer in [1,logn] such that Δf divides i (recall that Δh is (log(h)n)2 round to the next power of 2). We exploit this fact to design a space efficient structure for Type-A queries. Let λ[1,logn] be the integer, where Δλ+1<δ2Δλ. We now maintain the components in Section 3.2.2 for only selected values of h (with some modifications) as follows:

  1. 1.

    All values of h[1,λ1];

  2. 2.

    h=λ, but modify the value of Δh to δ2.

  3. 3.

    h=λ+1, but modify the value of Δh to δ.

The resulting space in bits is

2h=1λ+1nΔhlogΔh12nδlogδ2+2nδ2logΔλ1+2h=1λ1nΔhlogΔh1=O((n/δ)logδ).

Our previous algorithm (as described in Section 3.3) can be applied to determine PA[i] with the same time complexity. However, the topologies of suffix tree and prefix tree still takes O(n) bits. We replace them with the topologies of the sparse prefix tree (SPT) and sparse suffix tree (SST), which require less space. Additionally, we maintain the small space structure in Lemma 13 supporting LCE queries.

Replacing Prefix Tree with Sparse Prefix Tree (SPT)

We sample a subset of O(n/δ) nodes in the prefix tree as follows:

  1. 1.

    leaves k, where k is a multiple of δ;

  2. 2.

    the lowest common ancestor (LCA) of every pair of consecutive leaves sampled in step 1;

  3. 3.

    the leftmost and rightmost descendant leaves of sampled nodes obtained in step 2.

Let be the set of sampled leaves obtained after the above procedure. Then, the SPT is the compacted trie built from the reverse of circular prefixes corresponding only to the leaves in – that is, a sparse prefix tree. We maintain its topology in succinct space and a bit string B[1..n], where B[k]=1 iff k, as an indexible dictionary. The space required is O((n/δ)logδ) bits as there are 𝒪(nδ) 1’s in B. We now present some useful results.

Lemma 14.

Given two sampled leaves k,k in the prefix tree, the size of their LCA can be returned in O(1) time using SPT and the associated bit string.

Proof.

The sampled leaf k (resp., k) in the prefix tree corresponds to the leaf 𝗋𝖺𝗇𝗄B(k,1) (resp., 𝗋𝖺𝗇𝗄B(k,1)) in SPT. Therefore, find the range [s,e] of leaves in the subtree of the node LCA(𝗋𝖺𝗇𝗄B(k,1),𝗋𝖺𝗇𝗄B(k,1)) in SPT and return 𝗌𝖾𝗅𝖾𝖼𝗍B(e,1)𝗌𝖾𝗅𝖾𝖼𝗍B(s,1)+1.

The prefix tree topology play the following role in our earlier algorithm. Given i, (i) find which among the nodes u=LCA(if1,i) and u′′=LCA(i,if1′′) is lower (call it u) to decide which case to use in the decoding procedure and (ii) return the range of leaves [a,b] of u (note that we don’t require the pre-order rank of u). Since i,if1,if1′′ are all divisible by δ (hence sampled) in the case of Type-A queries, we can apply Lemma 14 and find the range of leaves of u and u′′ first, then infer u=u iff the range of u is the same or contained within the range of u′′. The subsequent steps in the recurrence also work since both ih and ih′′ are divisible by δ for all hf. In summary, a sparse prefix tree topology can perform the required functionalities in our algorithm without any performance slowdown.

Replacing Suffix Tree with Sparse Suffix Tree (SST)

The SST is the compacted trie constructed from the circular suffixes corresponding to the leaves k of the suffix tree, where k is a multiple of δ. We maintain its topology in succinct space, requiring O(n/δ) bits.

The suffix tree topology serves the purpose of finding the suffix range [𝗌𝗉(Q),𝖾𝗉(Q)] of Q in O(logn) time, given (i) the length of Q (this can be computed using pLCSπ as in Section 3.3), (ii) a leaf g in the suffix tree, where Q is a prefix of str(g), (iii) the size of the suffix range of Q, i.e., 𝖾𝗉(Q)𝗌𝗉(Q)+1 (specifically, 𝗌𝗂𝗓𝖾(u) is given, where u is a node in the prefix tree). It is worth noting that this task can be accomplished without the suffix tree topology, and even without the third input parameter. We can exploit the fact that LCE(SA[g],SA[p])|Q| if and only if p[𝗌𝗉(Q),𝖾𝗉(Q)], and hence we can apply binary search on p and determine the answer using O(logn) LCE queries.

We speed up this process by using the additional information in (iii) as follows. If 𝗌𝗂𝗓𝖾(u)2δ, we still apply the binary search. The number of LCE/SA queries required is O(logδ). Otherwise, find an approximate range using SST first, and then apply binary search on ranges of size O(δ) as follows.

  1. 1.

    Find a value g closer to g, such that g is a multiple of δ (thus g is sampled) and str(g) is also prefixed by Q as follows. Let g=δg/δ and g′′=δg/δ. We fix g=g if LCE(SA[g],SA[g])|Q|, otherwise g=g′′ (in this case LCE(SA[g],SA[g′′])|Q| is guaranteed as 𝗌𝗂𝗓𝖾(u)>2δ).

  2. 2.

    Find (g/δ)-th leaf in SST, and return the range [sp,ep] of leaves of its lowest ancestor with 𝗌𝗂𝗓𝖾()𝗌𝗂𝗓𝖾(u)/δ1. This step takes only O(logn) time (via 𝗅𝖾𝗏𝖾𝗅_𝖺𝗇𝖼𝖾𝗌𝗍𝗈𝗋 queries as before, but on SST). Observe that our sampling scheme guarantees 𝗌𝗉(Q)[δspO(δ),δsp] and 𝖾𝗉(Q)[δep,δep+O(δ)]. Therefore we can now restrict our binary search into a smaller range of size O(δ), requiring only O(logδ) LCE and SA queries.

This subroutine for finding suffix range [𝗌𝗉(Q),𝖾𝗉(Q)] of Q takes O(logn+(tsa+tLCE)logδ)O(logn+(δtwt+tsa)δlogδ) time. After that, we continue with steps (b), (c), and (d) as in Section 3.3.

Figure 2: Illustration of Type-B queries.

By substituting the space–time complexities of the components, incorporating updated data structures, we obtain the following bound for Type-A queries.

Lemma 15 (Type-A Queries).

By maintaining auxiliary structures of space O((n/δ)logδ) bits in total, we can answer Type-A queries in time

O((logn+tquantile+(δtwt+tsa)δlogδ)logn).

4.2.3 Handling Type-B Queries

Type-B queries require a more involved solution than Type-A queries. Maintain the structure in Lemma 11 supporting pLCSπ queries, for values of π in {1,+1}. Also associate O(logδ) bits of satellite information (to be defined shortly) with each integer in [1,n] that is a multiple of δ. The total space is O((n/δ)logδ) bits.

To retrieve PA[i], where i is not divisible by δ, we employ a procedure that decodes a segment of PA containing PA[i]. Let i=δi/δ, i′′=δi/δ=i+δ, and u=LCA(i,i′′). We now proceed to define some nodes in the prefix tree as illustrated in Figure 2.

  • x1,x2,,xd are nodes that lie on the path from i to u (excluding endpoints), ordered in descending 0pt(), such that x0=i and xk for k[1,d] is the lowest ancestor of xk1 whose child on the path to i has a right sibling. Let [,bk] denote the range of leaves of xk for k[0,d]. Therefore, (i,bd] denotes the range of leaves after i, such that the LCA of any of those leaves with i is a node in {x1,x2,,xd}. The following observation allows us to jump from node xk1 to node xk for all k[1,d].

Observation 16.

For all k[1,d] we have LCA(bk1,bk1+1)=xk; therefore strlen(xk)=pLCS+1[PA[bk1]].

  • y1,y2,,yf are nodes that lie on the path from i′′ to u (excluding endpoints), ordered by descending 0pt(), such that y0=i′′ and yk for k[1,f] is the lowest ancestor of yk1 whose child on the path to i′′ has a left sibling. Let [ak,] denote the range of leaves of yk for k[0,f]. Therefore, [af,i′′) denotes the range of leaves before i′′, such that the LCA of any of those leaves with i′′ belongs to {y1,y2,,yf}. The following observation allows us to jump from node yk1 to node yk for all k[1,f].

Observation 17.

For all k[1,f], we have LCA(ak1,ak11)=yk; therefore strlen(yk)=pLCS1[PA[ak1]].

Our procedure for decoding PA[i] depends on which of the intervals (i,bd],[bd+1,af1], or [af,i′′) contains i. The satellite information associated with i is (d,f,bdi,afi) (all four values are bounded by δ), which helps to decide the case.

CASE 𝒊(𝒃𝟎,𝒃𝒅]

First obtain PA[i] using a Type-A query, then initialize b0=i and strlen(x0)=PA[b0], the length of the prefix ending at b0. Then decode PA(b0,bd] in d steps; at step k[1,d], we compute strlen(xk),𝗌𝗂𝗓𝖾(xk),bk and PA(bk1,bk]. Note that (b0,bd]=(b0,b1](b1,b2](bd1,bd]. Let Qt be the prefix of str(i) of length t and αt be the (t+1)-th character of str(i). Therefore, Qt=𝖳[PA[i]t+1..PA[i]] and αt=𝖳[PA[i]t]. When t=strlen(xk), we let Q=Qt and α=αt.

  1. 1.

    Finding 𝐬𝐭𝐫𝐥𝐞𝐧(xk): Observe that strlen(xk) is the largest t<strlen(xk1) such that BWT[𝗌𝗉(Qt),𝖾𝗉(Qt)] contains a character βt>αt. However, finding [𝗌𝗉(Qt),𝖾𝗉(Qt)] can be expensive. Thankfully, we have an alternative procedure to verify this condition for a given t. Find z=ISA[PA[i]t+1], which is an entry in [𝗌𝗉(Qt),𝖾𝗉(Qt)], then find the largest number z before z, and the smallest number z′′ after z, such that BWT[z],BWT[z′′]>αt=BWT[z], using wavelet tree queries. The condition is satisfied if and only if LCE(ISA[z],ISA[z])t or LCE(ISA[z],ISA[z′′])t. We next determine an l such that strlen(xk)[lδ,l], meaning we only need to iterate over the values of t in [lδ,l] for finding strlen(xk). To find l, we rely on observation 16 that xk=LCA(bk1,bk1+1) and strlen(xk)=pLCS[PA[bk1]]. Therefore, pLCS1[PA[bk1]]strlen(xk)pLCS1[PA[bk1]]+δ, and we compute pLCS1[PA[bk1]] in O(logδ) time (see Lemma 11), and set l=min{strlen(xk1)1,pLCS1[PA[bk1]]+δ}. The total time is O((twt+tsa+tLCE)δ)O(tLCEδ).

  2. 2.

    Finding 𝗌𝗂𝗓𝖾(xk): At first, we find an approximation 𝗌𝗂𝗓𝖾(xk)[𝗌𝗂𝗓𝖾(xk)2δ,𝗌𝗂𝗓𝖾(xk)] using SPT and the associated bit vector B as follows. Note that xk=LCA(i,bk1+1) and the leaf i is sampled (recall SPT’s construction). Therefore, if bk1+1 is also sampled (i.e., B[bk1+1]=1), then we can find the exact value of 𝗌𝗂𝗓𝖾(xk) in O(1) time using Lemma 14. Otherwise, find the leaves 𝗋𝖺𝗇𝗄B(bk1+1,1),𝗋𝖺𝗇𝗄B(bk1+1,1)+1 in SPT, the child of their LCA on the path to 𝗋𝖺𝗇𝗄B(i,1), and the range [s,s] of leaves of that child. Then return 𝗌𝖾𝗅𝖾𝖼𝗍B(s,1)𝗌𝖾𝗅𝖾𝖼𝗍(s,1)+1 as 𝗌𝗂𝗓𝖾(xk). Our sampling scheme guarantees that the value returned lies in the range [𝗌𝗂𝗓𝖾(xk)2δ,𝗌𝗂𝗓𝖾(xk)]. The time complexity is O(1). We now proceed to computing the exact value of 𝗌𝗂𝗓𝖾(xk). Since Q=str(xk), we have 𝗌𝗂𝗓𝖾(xk)=𝖾𝗉(Q)𝗌𝗉(Q)+1. Therefore we compute the range [𝗌𝗉(Q),𝖾𝗉(Q)] as follows. To begin with, we know an entry ISA[PA[i]|Q|+1][𝗌𝗉(Q),𝖾𝗉(Q)], as well as 𝗌𝗂𝗓𝖾(xk). At the point, we can follow the same procedure detailed in Section 4.2.2, where we first identify an approximate range that is sufficiently close to the actual one using sparse suffix tree in O(logn) time, then perform a binary search requiring only O(logδ) LCE and ISA queries. The time required is O(logn+(tsa+tLCE)logδ)O(logn+tLCElogδ).

  3. 3.

    Finding bk: Let α be the leading character on the path from xk to its child on the path to i. Count the entries in BWT[𝗌𝗉(Q),𝖾𝗉(Q)] that are above α=𝖳[PA[i]|Q|] (using a wavelet tree query). This gives the number of leaves in the range (bk1,bk]. Therefore we simply add bk1 with this count and return as bk. Time required is O(twt).

  4. 4.

    Finding 𝐏𝐀(bk𝟏,bk]: The set 𝒫={SA[z]+|Q|1z[𝗌𝗉(Q),𝖾𝗉(Q)],BWT[z]>α} is a collection of elements in PA(bk1,bk], which can be obtained in O(twt+tsa) time per element using wavelet tree and SA[] queries. However, we do not know the order in which they appear in PA(bk1,bk]; therefore our next goal is to recover PA(bk1,bk] by sorting the elements in 𝒫 in the ascending order of their IPA[] values. We first present a simple solution, and then describe a slightly improved one.

    1. (a)

      A simple solution using 𝐋𝐂𝐄(,) queries: By fixing D=δlogn, we maintain the structure in Lemma 4 without changing the asymptotic space complexity, which supports LCE(,) queries in O(tLCElog(δlogn)) time. Then, for any p,q𝒫, we can decide the relative order of IPA[p] and IPA[q] by comparing the characters 𝖳[pLCE(p,q)] and 𝖳[qLCE(p,q)]. Therefore, an efficient comparison-based sorting algorithm (such as merge sort) can be used to sort the elements of 𝒫 in ascending order of their IPA[] values using O((bkbk1)log(bkbk1)) number of LCE(,) queries. The resulting time complexity for this step is

      (bkbk1)log(bkbk1)tLCE(log(δlogn)O((bkbk1)logδtLCE(logδ+loglogn)).
    2. (b)

      An alternative solution: Our goal here is to avoid the direct use of LCE(,) queries, and thereby removing the loglogn term in the previous time complexity. To achieve this, we reconstruct the subtree rooted at xk on the fly. For any node w in the subtree rooted at xk, let 𝒫(w)𝒫 denotes the set of PA[] values corresponding to the leaves in the subtree of w. Iterating over p𝒫, our algorithm return the sets 𝒫(w1),𝒫(w2),, in that order, where nodes w1,w2, are the children (in the left to right order) of node w=LCA(IPA[p],IPA[p]+1). Note that the preorder rank of w is not known even after processing p. Therefore, based on 𝒫(w), we assign to w a unique label, defined as the ordered pair consisting of the size of 𝒫(w) and the smallest element of 𝒫(w). With this labeling and parent–child information for all internal nodes, we can easily reconstruct the tree in a top-down fashion and recover PA(bk1,bk]. We now present the details of processing a specific p𝒫. The first step is to compute strlen(w)=pLCS[p], which is given by the expression below when pPA[bk] (in that case PA[IPA[p]+1]𝒫); when p=PA[bk], it will not return any value.

      max{LCE(p,q)q𝒫\{p},T[1..q] comes after T[1..p] lexicographically}

      Instead of evaluating this expression by applying LCE(,) directly, we compute L=pLCS[p] in O(logδ) time (see Lemma 11), where pLCS[p]δLpLCS[p]. Then, iterate over q𝒫, compute LCE(pL+1,qL+1), and consider the following two cases:

      1. i.

        If LCE(pL+1,qL+1)<L, then we know LCE(p,q)<Lstrlen(w). Therefore discard q.

      2. ii.

        Otherwise we have LCE(p,q)=L+LCE(pL,qL). In that case, extract the substrings 𝖳[pLδ..pL] and 𝖳[qLδ..qL] and compare them. If the substrings differ, we can compute LCE(pL,qL) (thus compute LCE(p,q)) and determine their lexicographic order (and thus the lexicographic order of T[1..q] and T[1..p]). If the substrings match, we infer that LCE(p,q)>L+δstrlen(w), and therefore both IPA[q] and IPA[p] lie in the subtree of the same child of w, and T[1..q] comes before T[1..p] in lexicographically.

      The above step for a specific p,q takes O(tLCE+tsa+δtwt) time. By taking the maximum value of LCE(p,q) over all q that satisfy the required condition on the lexicographic order, we obtain strlen(w). We next collect all positions q such that LCE(p,q)strlen(w), group them into sets according to the character T[qstrlen(w)], and output these sets in order of increasing character value they correspond to. It is straightforward to verify that the sets produced in this manner are exactly 𝒫(w1),𝒫(w2),. Moreover, every internal node in the subtree rooted at xk will be processed after we have iterated over all p𝒫\{PA[bk]}. The overall time complexity of this step is O((bkbk1)2(tLCE+tsa+δtwt))O((bkbk1)2tLCE).

    We use the second method, although its time complexity is quadratic in the size of the tree, because the first method would add extra loglogn factors to the time complexity.

The combined time for all 4 steps for all k[1,d], where dδ is bounded as follows:

k=1d(tLCEδ+(logn+tLCElogδ)+twt+(bkbk1)2tLCE)
tLCEδ2+(logn+tLCElogδ)δ+twtδ+(bdb0)2tLCE

By substituting bdb0=O(δ) and tLCE=O((tsa+δtwt)δ), the expression simplifies to O(δlogn+(tsa+δtwt)δ3).

CASE 𝒊[𝒃𝒅+𝟏,𝒂𝒇𝟏]

Let Q=str(u) and α be the leading character on the path from u to its child on the path to i and β be the leading character on the path from u to its child on the path to i′′. Then the set 𝒫={SA[z]+|Q|1z[𝗌𝗉(Q),𝖾𝗉(Q)],BWT[z][α+1,β1]} is the collection of elements in PA[bd+1,af1]. Since 𝗌𝗂𝗓𝖾(u), strlen(u),PA[i],PA[i′′] are readily available (via Type-A query), the elements in the set 𝒫 can be easily extracted in time O(twt+tsa) per element, but not in the order as they appear in PA. Finally, to recover PA[bd+1,af1], we follow the same procedure as in the first case. The resulting time is O((tsa+δtwt)δ3).

CASE 𝒊[𝒂𝒇,𝒊′′)

Here we obtain PA[i′′] first using a Type-A query, then initialize a0=i′′ and strlen(y0)=PA[a0], the length of the prefix ending at a0. Then decode PA[af,a0) in f steps, where at step k[1,f], we compute strlen(yk), 𝗌𝗂𝗓𝖾(yk), ak and PA[ak,ak1). Note that [af,a0)=[af,af1)[a2,a1)[a1,a0). Suppose Q=str(yk) and β=𝖳[PA[i′′]|Q|], then the set elements in PA[ak,ak1) is given by 𝒫={SA[z]+|Q|1z[𝗌𝗉(Q),𝖾𝗉(Q)],BWT[z]<β}. This is analogous to that of the first case, therefore we invoke a symmetric procedure to handle this case. In contrast to the first case, where we rely on Observation 16 and use pLCS1[] queries, here we rely on Observation 17 and use pLCS1[] queries. Total time is O(δlogn+(tsa+δtwt)δ3) time (similar to the first case).

Lemma 18 (Type-B Queries).

The combined space of all auxiliary structures for Type-B queries is O((n/δ)logδ) bits and the query time is O(δlogn+(δtwt+tsa)δ3).

By combining Lemma 15 and Lemma 18, we obtain the Theorem below.

Theorem 19.

Let the Burrows-Wheeler Transform (BWT) of a text 𝖳[1..n] be maintained in a data structure that supports basic queries (𝗋𝖺𝗇𝗄, 𝗌𝖾𝗅𝖾𝖼𝗍, and 𝖺𝖼𝖼𝖾𝗌𝗌) in twt time, and range quantile queries in tquantile time. Then, for any parameter τ1, we can maintain a sampled subset of n/τ suffix array values using O((n/τ)logn) bits of additional space, and support suffix array (SA) and inverse suffix array (ISA) queries in time tsa=O(τtwt). By maintaining an auxiliary structure of space O((n/δ)logδ) bits for a parameter δ, we can support prefix array (PA) queries in time

tpa=O((logn(logn+δ)+tquantilelogn+(δ+τ)twt(δlogδlogn+δ3))
O((logn+tquantile+(δ+τ)twt)(δlogδlogn+δ3)).

Additionally we can support LCE(,) queries in time tLCE=O((δtwt+tsa)δ) and LCE(,) queries in time O(tLCE(logδ+loglogn)).

By employing the multiary wavelet tree, where twt=O(1+logσ/loglogn), tquantile=O(logσ) and setting δ=κlogκ and τ=κlogn, we obtain the following result.

Corollary 20.

Let 𝖳[1..n] be a text over an alphabet of size σ. We can maintain its Burrows-Wheeler Transform (BWT) as a wavelet tree data structure in nlogσ+o(n)+O(σlogn) bits that supports basic queries (rank, select, access) in time O(1+logσ/loglogn) and range quantile queries in time O(logσ). For an integer parameter κ, we can maintain an auxiliary structure of space O(n/κ) bits and support suffix array (SA) and inverse suffix array (ISA) queries in time tsa=O((1+logσ/loglogn)κlogn), and prefix array (PA) queries in time tpa=tsaO((κ3logκ+κlogn)log2κ).

Additionally we can support LCE(,) queries in time tLCE=O(tsaκlogκ) and LCE(,) queries in time O(tLCE(logκ+loglogn)).

5 A Note On Inverse Prefix Array (IPA) Queries

We present a simple approach for supporting IPA[] queries. Assuming the availability of data structures supporting PA[] and LCE(,) queries, we maintain the auxiliary structure from Lemma 4 with the parameter set to D=κlogn. This structure requires O(n/κ) additional bits of space and supports LCE(,) queries in LCE(,) time O(tLCElog(κlogn)).

Given a text position j, we compute IPA[j] by performing a binary search over i[1,n]. For a candidate index i, we first compute x=PA[i]. If x=j, we return i and terminate the search. Otherwise, we compute l=LCE(x,j) and compare the characters 𝖳[xl] and 𝖳[jl] (which must be different). If 𝖳[xl]<𝖳[jl], then we conclude that i<IPA[j]; otherwise, i>IPA[j]. This provides the necessary comparison predicate for binary search. The time complexity is O((tpa+tLCElog(κlogn))logn)O(tpalognloglogn).

Theorem 21.

With the structure described in Theorem 19, we can maintain an auxiliary data structure of space O(n/κ) bits that supports inverse prefix array (IPA) queries in time O(tpalognloglogn).

6 Conclusions

We present a solution for encoding the suffix array and inverse suffix array of the reversed text, assuming the availability of the FM-index of the original text. We can answer suffix array queries on the reversed text in time close to that of suffix array queries on the original text. However, for inverse suffix array queries on the reversed text, we require roughly a factor of (logn) more time than for inverse suffix array queries on the original text. Narrowing this gap is an interesting problem, which we leave open for future research.

References

  • [1] Paniz Abedin, Oliver A. Chubet, Daniel Gibney, and Sharma V. Thankachan. Contextual pattern matching in less space. In Data Compression Conference, DCC 2023, Snowbird, UT, USA, March 21-24, 2023, pages 160–167. IEEE, 2023. doi:10.1109/DCC55655.2023.00024.
  • [2] Amihood Amir, Dmitry Keselman, Gad M. Landau, Moshe Lewenstein, Noa Lewenstein, and Michael Rodeh. Text indexing and dictionary matching with one error. J. Algorithms, 37(2):309–325, 2000. doi:10.1006/JAGM.2000.1104.
  • [3] Djamal Belazzougui, Travis Gagie, Simon Gog, Giovanni Manzini, and Jouni Sirén. Relative fm-indexes. In String Processing and Information Retrieval: 21st International Symposium, SPIRE 2014, Ouro Preto, Brazil, October 20-22, 2014. Proceedings 21, pages 52–64. Springer, 2014. doi:10.1007/978-3-319-11918-2_6.
  • [4] Philip Bille, Inge Li Gørtz, Mathias Bæk Tejs Knudsen, Moshe Lewenstein, and Hjalte Wedel Vildhøj. Longest common extensions in sublinear space. In Ferdinando Cicalese, Ely Porat, and Ugo Vaccaro, editors, Combinatorial Pattern Matching - 26th Annual Symposium, CPM 2015, Ischia Island, Italy, June 29 - July 1, 2015, Proceedings, volume 9133 of Lecture Notes in Computer Science, pages 65–76. Springer, 2015. doi:10.1007/978-3-319-19929-0_6.
  • [5] Michael Burrows. A block-sorting lossless data compression algorithm. SRS Research Report, 124, 1994.
  • [6] Andrea Farruggia, Travis Gagie, Gonzalo Navarro, Simon J Puglisi, and Jouni Sirén. Relative suffix trees. The Computer Journal, 61(5):773–788, 2018. doi:10.1093/COMJNL/BXX108.
  • [7] Paolo Ferragina and Giovanni Manzini. Opportunistic data structures with applications. In Proceedings of the 41st Annual Symposium on Foundations of Computer Science, pages 390–398. IEEE, 2000. doi:10.1109/SFCS.2000.892127.
  • [8] Paolo Ferragina, Giovanni Manzini, Veli Mäkinen, and Gonzalo Navarro. Compressed representations of sequences and full-text indexes. ACM Trans. Algorithms, 3(2):20, 2007. doi:10.1145/1240233.1240243.
  • [9] Johannes Fischer and Volker Heun. Space-efficient preprocessing schemes for range minimum queries on static arrays. SIAM J. Comput., 40(2):465–492, 2011. doi:10.1137/090779759.
  • [10] Travis Gagie, Gonzalo Navarro, and Nicola Prezza. Fully functional suffix trees and optimal text searching in BWT-runs bounded space. J. ACM, 67(1):2:1–2:54, 2020. doi:10.1145/3375890.
  • [11] Travis Gagie, Simon J. Puglisi, and Andrew Turpin. Range quantile queries: Another virtue of wavelet trees. In String Processing and Information Retrieval (SPIRE), volume 5721 of Lecture Notes in Computer Science, pages 1–6. Springer, Berlin, Heidelberg, 2009. arXiv:0903.4726. doi:10.1007/978-3-642-03784-9_1.
  • [12] Arnab Ganguly, Daniel Gibney, Sahar Hooshmand, M. Oguzhan Külekci, and Sharma V. Thankachan. Fm-index reveals the reverse suffix array. In Inge Li Gørtz and Oren Weimann, editors, 31st Annual Symposium on Combinatorial Pattern Matching, CPM 2020, Copenhagen, Denmark, June 17-19, 2020, volume 161 of LIPIcs, pages 13:1–13:14. Schloss Dagstuhl – Leibniz-Zentrum für Informatik, 2020. doi:10.4230/LIPIcs.CPM.2020.13.
  • [13] Roberto Grossi, Ankur Gupta, and Jeffrey Scott Vitter. High-order entropy-compressed text indexes. In Proceedings of the 14th Annual ACM-SIAM Symposium on Discrete Algorithms, pages 841–850. SIAM, 2003. URL: http://dl.acm.org/citation.cfm?id=644108.644250.
  • [14] Roberto Grossi and Jeffrey Scott Vitter. Compressed suffix arrays and suffix trees with applications to text indexing and string matching. SIAM J. Comput., 35(2):378–407, 2005. doi:10.1137/S0097539702402354.
  • [15] Jesper Jansson, Kunihiko Sadakane, and Wing-Kin Sung. Ultra-succinct representation of ordered trees with applications. J. Comput. Syst. Sci., 78(2):619–631, 2012. doi:10.1016/J.JCSS.2011.09.002.
  • [16] Dominik Kempa and Tomasz Kociumaka. Collapsing the hierarchy of compressed data structures: Suffix arrays in optimal compressed space. In 64th IEEE Annual Symposium on Foundations of Computer Science, FOCS 2023, Santa Cruz, CA, USA, November 6-9, 2023, pages 1877–1886. IEEE, 2023. doi:10.1109/FOCS57990.2023.00114.
  • [17] Tak Wah Lam, Ruiqiang Li, Alan Tam, Simon C. K. Wong, Edward Wu, and Siu-Ming Yiu. High throughput short read alignment via bi-directional BWT. In 2009 IEEE International Conference on Bioinformatics and Biomedicine, BIBM 2009, Washington, DC, USA, November 1-4, 2009, Proceedings, pages 31–36. IEEE Computer Society, 2009. doi:10.1109/BIBM.2009.42.
  • [18] Ben Langmead, Cole Trapnell, Mihai Pop, and Steven L. Salzberg. Ultrafast and memory-efficient alignment of short dna sequences to the human genome. Genome Biology, 10(3):R25, 2009.
  • [19] Moshe Lewenstein. Orthogonal range searching for text indexing. In Andrej Brodnik, Alejandro López-Ortiz, Venkatesh Raman, and Alfredo Viola, editors, Space-Efficient Data Structures, Streams, and Algorithms - Papers in Honor of J. Ian Munro on the Occasion of His 66th Birthday, volume 8066 of Lecture Notes in Computer Science, pages 267–302. Springer, 2013. doi:10.1007/978-3-642-40273-9_18.
  • [20] Heng Li and Richard Durbin. Fast and accurate short read alignment with burrows-wheeler transform. Bioinformatics, 25(14):1754–1760, 2009. doi:10.1093/BIOINFORMATICS/BTP324.
  • [21] Udi Manber and Gene Myers. Suffix arrays: A new method for on-line string searches. SIAM Journal on Computing, 22(5):935–948, 1993. doi:10.1137/0222058.
  • [22] Edward M. McCreight. A space-economical suffix tree construction algorithm. Journal of the ACM, 23(2):262–272, 1976. doi:10.1145/321941.321946.
  • [23] J. Ian Munro and Venkatesh Raman. Succinct representation of balanced parentheses and static trees. SIAM J. Comput., 31(3):762–776, 2001. doi:10.1137/S0097539799364092.
  • [24] Gonzalo Navarro. Wavelet trees for all. J. Discrete Algorithms, 25:2–20, 2014. doi:10.1016/J.JDA.2013.07.004.
  • [25] Gonzalo Navarro. Contextual pattern matching. In International Symposium on String Processing and Information Retrieval, pages 3–10. Springer, 2020. doi:10.1007/978-3-030-59212-7_1.
  • [26] Gonzalo Navarro. Indexing highly repetitive string collections, part I: repetitiveness measures. ACM Comput. Surv., 54(2):29:1–29:31, 2022. doi:10.1145/3434399.
  • [27] Gonzalo Navarro. Indexing highly repetitive string collections, part II: compressed indexes. ACM Comput. Surv., 54(2):26:1–26:32, 2022. doi:10.1145/3432999.
  • [28] Mihai Pătraşcu. Succincter. In 49th Annual IEEE Symposium on Foundations of Computer Science, FOCS 2008, October 25-28, 2008, Philadelphia, PA, USA, pages 305–313. IEEE Computer Society, 2008. doi:10.1109/FOCS.2008.83.
  • [29] Rajeev Raman, Venkatesh Raman, and Srinivasa Rao Satti. Succinct indexable dictionaries with applications to encoding k-ary trees, prefix sums and multisets. ACM Trans. Algorithms, 3(4):43, 2007. doi:10.1145/1290672.1290680.
  • [30] Luís MS Russo, Gonzalo Navarro, and Arlindo L Oliveira. Fully-compressed suffix trees. In LATIN 2008: Theoretical Informatics: 8th Latin American Symposium, Búzios, Brazil, April 7-11, 2008. Proceedings 8, pages 362–373. Springer, 2008. doi:10.1007/978-3-540-78773-0_32.
  • [31] Kunihiko Sadakane. Compressed suffix trees with full functionality. Theory Comput. Syst., 41(4):589–607, 2007. doi:10.1007/S00224-006-1198-X.
  • [32] Dirk Strothmann. The affix array data structure and its applications to RNA secondary structure analysis. Theor. Comput. Sci., 389(1-2):278–294, 2007. doi:10.1016/J.TCS.2007.09.029.
  • [33] Esko Ukkonen. On-line construction of suffix trees. Algorithmica, 14(3):249–260, 1995. doi:10.1007/BF01206331.
  • [34] Peter Weiner. Linear pattern matching algorithms. 14th Annual Symposium on Switching and Automata Theory, pages 1–11, 1973. doi:10.1109/SWAT.1973.13.