String Indexing for Top-$k$ Close Consecutive Occurrences

The classic string indexing problem is to preprocess a string $S$ into a compact data structure that supports efficient subsequent pattern matching queries, that is, given a pattern string $P$, report all occurrences of $P$ within $S$. In this paper, we study a basic and natural extension of string indexing called the string indexing for top-$k$ close consecutive occurrences problem (SITCCO). Here, a consecutive occurrence is a pair $(i,j)$, $i<j$, such that $P$ occurs at positions $i$ and $j$ in $S$ and there is no occurrence of $P$ between $i$ and $j$, and their distance is defined as $j-i$. Given a pattern $P$ and a parameter $k$, the goal is to report the top-$k$ consecutive occurrences of $P$ in $S$ of minimal distance. The challenge is to compactly represent $S$ while supporting queries in time close to the length of $P$ and $k$. We give three time-space trade-offs for the problem. Let $n$ be the length of $S$, $m$ the length of $P$, and $\epsilon\in(0,1]$. Our first result achieves $O(n\log n)$ space and optimal query time of $O(m+k)$. Our second and third results achieve linear space and query times either $O(m+k^{1+\epsilon})$ or $O(m + k \log^{1+\epsilon} n)$. Along the way, we develop several techniques of independent interest, including a new translation of the problem into a line segment intersection problem and a new recursive clustering technique for trees.


Introduction
The classic string indexing problem is to preprocess a string S into a compact data structure that supports efficient subsequent pattern matching queries, that is, given a pattern string P , report all occurrences of P within S.An occurrence of P within S is an index i, 0 ≤ i < |S|, such that P = S[i . . .i + |P | − 1].In this paper, we introduce a basic extension of string indexing, where the goal is to report consecutive occurrences of the pattern P that occur close to each other in S. Here, a consecutive occurrence is a pair (i, j), i < j, such that P occurs at positions i and j in S and there is no occurrence of P between i and j, and close to each other means that the distance j − i between the occurrences should be small.More precisely, given a pattern P and an integer parameter k > 0, define the top-k close consecutive occurrences of P to be the k consecutive occurrences of P in S with the smallest distances.Given a string S the string indexing for top-k close consecutive occurrences (Sitcco) problem is to preprocess S into a data structure that supports top-k close consecutive occurrences queries.The goal is to obtain a compact data structure while supporting fast queries in terms of the length of the pattern P and the number of reported occurrences k.For an example, see Figure 1.4,7,11,22,24,26,30,39 and 41 in S. The top 5 close consecutive occurrences are (22,24), (24,26), (39,41), (4,7), and (7,11), with the tie between (7,11) and (26,30) broken arbitrarily.
Surprisingly, the Sitcco problem has not been studied before even though it is a natural variant of string indexing and several closely related problems have been extensively studied (see related work below).

Results and Techniques
To state the complexity bounds, let n and m denote the lengths of S and P , respectively.An immediate approach to solve the Sitcco problem is to store the suffix tree of S using O(n) space.To answer a query on P with parameter k, we traverse the suffix tree to find all occurrences of P , construct the consecutive occurrences, and then sort these to output the top-k close consecutive occurrences.Naively, this requires two sorts of size occ, where occ is the total number of occurrences of P , giving a query time of O(m+occ log occ).Using more advanced data structures [12,13], the query time can be reduced to O(m + occ) while still using linear space.Note that occ can be much larger than k.Alternatively, we can store at every node in the suffix tree the set of all consecutive occurrences sorted by distance using O(n 2 ) space.To answer a query we find the node corresponding to P and simply report the first k of the stored consecutive occurrences in optimal O(m + k) time.
To achieve better trade-offs, one might try to use a strategy similar to range minimum query (RMQ), where the ranges are subsequent ranges in the suffix array and the values are distances between pairs of suffix indexes in S.However, there are several problems with that idea: first, there are Θ(|S| 2 ) possible pairs of suffix indexes within S, and it is not immediately clear how many of them can correspond to consecutive occurrences of a pattern (our arguments from Section 3 imply that this number is bounded by O(n log n)).Secondly, when taking the union of two ranges, the set of closest (consecutive) pairs can change completely: consider for example the string S = A B A C A B A C D A B D A C D A B D A C. While the string A has occurrences {0, 2, 4, 6, 9, 12, 15, 18}, the string AB has occurrences {0, 4, 9, 15} and AC has {2, 6, 12, 18}.Note that for P = A, the top-3 consecutive occurrences are (0, 2), (2,4) and (4,6), while for AB they are (0, 4), (4,9) and (9,15) and for AC they are (2,6), (6,12) and (12,18).Both the pairs and the distances are completely different between A and its extensions.Thus, there is an issue of non-decomposability, which is a main challenge in this particular problem.However, in the rest of our paper we will show that we can use suffix tree decompositions and amortized arguments to bound the number of changes that can happen in the set of consecutive occurrences of substrings corresponding to positions on some paths in the suffix tree.
We obtain the following significantly improved time-space trade-offs: Theorem 1.Given a string S of length n and ϵ, 0 < ϵ ≤ 1, we can build a data structure that can answer top-k close consecutive occurrences queries using either Here, m is the length of the query pattern.
To achieve Theorem 1 we develop several data structural techniques that may be of independent interest.First, we translate the problem into a line segment intersection problem on the heavy path decomposition of the suffix tree.This leads to the O(n log n) space and optimal query time bound of Theorem 1(i).We note that Navarro and Thankachan [34] used similar techniques for a closely related problem (see related work below).To reduce space, we introduce a novel recursive clustering method on trees.The decomposition partitions the tree into a hierarchy of depth O(log log n) consisting of subtrees of doubly exponentially decreasing sizes.We show how to combine the decomposition with the techniques of the simple algorithm from Theorem 1(i) to obtain an O(n log log n) space and O(m + k 1+ϵ ) query time solution.Then, we show how to efficiently compress the hierarchy of data structures into rank space leading to the linear space and O(m + k 1+ϵ ) query time bound of Theorem 1(ii).Finally, we show how to use O(log n) cluster decompositions of varying parameters together with an orthogonal range successor data structure to obtain the O(m + k log 1+ϵ ) time bound of Theorem 1(iii).
We apply these techniques to three related problems: Firstly, we address the natural "opposite" problem of reporting the k consecutive occurrences of largest distance, which can be solved using similar but not identical techniques.Secondly, we apply our framework to the related problem of reporting consecutive occurrences with distances within a specified interval, considered by Navarro and Thankachan [34], and give an improvement for a special case.Finally, we show how this allows us to efficiently report all non-overlapping consecutive occurrences of a pattern.

Related Work
To the best of our knowledge, the Sitcco problem has not been studied before, even though distances between occurrences is a natural extension for string indexing and several related problems have been studied extensively.
A closely related problem was considered by Navarro and Thankachan [34], who showed how to efficiently report consecutive occurrences with distances within a specified interval.They gave an O(n log n) space and O(m + occ) time solution, where occ denotes the number of reported consecutive occurrences.We note that their result can be adapted to the Sitcco problem to achieve the same bounds as in Theorem 1(i).However, our solution is simpler and does not rely on heavy word RAM techniques such as persistent van Emde Boas trees [15].Our techniques can also be used to solve the problem considered by Navarro and Thankachan getting the same space and time bounds as they obtain, and we can achieve improved bounds in a special case (see Section 8).
A lot of work has been done on the related problem of string indexing for patterns under various distance constraints, where the goal is to report occurrences of (one or more) patterns that are within a given distance or interval of distances of each other [4,6,10,14,26,27,28].An important difference between those works and our work is that all those solutions use time proportional to all pairs of occurrences with distances in the given range, in contrast to only finding consecutive occurrences.Note that if the goal is to find occurrences of a given maximal distance, one can find the close consecutive occurrences first and then construct all pairs satisfying the constraint.
Another line of related work is indexing collections of strings, called documents.Here the goal is to find documents containing patterns subject to various constraints.For a comprehensive overview see the survey by Navarro [31].Several results on supporting efficient top-k queries are known [11,21,22,23,23,24,29,30,32,33,36,38].In this context the goal is to efficiently report the k documents of smallest weight.The weights can depend on the query and can be the distance between the closest pair of occurrences of a given pattern [23,23,29,32,32,36].The problem can be solved in linear space and optimal O(k) time, in addition to finding the locus of the pattern in the suffix tree [36].While this problem statement resembles ours, there is no direct translation from those results to our problem, since the documents are considered individually, and for a single document only the pair of occurrences with minimum distance within the document is considered.
Finally, we note that since the initial publication of the results in this paper, a subset of the authors have recently considered indexing for consecutive occurrences of two different patterns P1 and P2 [8].

Outline
The paper is organized as follows.In Section 2 we introduce some notation and recall results on string indexing.In Section 3 we build a simple data structure and prove Theorem 1(i).In Section 4 we recall a method for tree clustering and show how to use it to solve a simplified version of the problem.In Section 5 we introduce a recursive clustering method that allows us to use the ideas from Section 4 on the actual problem.This gives an O(n log log n) space and O(m + k 2 ) time data structure.In Section 6, we show how to reduce the space to linear while achieving the same query time, and then generalize the recursion to get Theorem 1(ii) for any 0 < ϵ ≤ 1.
In Section 7 we give the linear space solution with O(m + k log 1+ϵ n) query time.Finally, in Section 8 we apply our techniques to related problems.

Preliminaries
We introduce some notation and recall basic results from string indexing.
A string S of length n is a sequence S[0]S [1] . . .S[n − 1] of characters from an alphabet Σ.A contiguous subsequence S[i, j] = S[i]S[i + 1] . . .S[j − 1] is a substring of S. The substrings of the form S[i, n] are the suffixes of S.
The suffix tree [39] is a compact trie of all suffixes of S$, where $ is a symbol not in the alphabet, and is lexicographically smaller than any letter in the alphabet.Using perfect hashing [19], it can be stored in O(n) space and solve the string indexing problem (i.e., find and report all occurrences of a pattern P ) in O(m + occ) time, where m is the length of P and occ is the number of times P occurs in S. The suffix array stores the suffix indices of S$ in lexicographic order.The suffix tree has the property that the leaves below any node represent suffixes that appear in consecutive order in the suffix array.Brodal et al. [13] show that there is a linear space data structure that allows outputting all entries within a given range of an array in sorted order using time linear in size of the output.This data structure on the suffix array together with the suffix tree can output all occurrences of a pattern sorted by text order in O(n) space and O(m + occ) time.
For any node v in the suffix tree, we define str(v) to be the string found by concatenating all labels on the path from the root to v. The locus of a string P , denoted locus(P ), is the minimum depth node v such that P is a prefix of str(v).

A Simple O(n log n) Space Solution
In this section, we present a simple solution that solves the Sitcco problem in O(n log n) space and O(m + k) query time.This solution will be a key component in our more advanced structures in the following sections.We note that the results by Navarro and Thankachan [34] for the related problem of reporting consecutive occurrences with distances within a specified interval can be modified to achieve the same complexities.However, our solution is simpler and does not rely on heavy word RAM techniques such as persistent van Emde Boas trees [15].
Let D(v) denote the set of consecutive occurrences of str(v).Naively, if we store for each node v the set D(v) in sorted order, we can directly answer a query for the top-k close consecutive occurrences of a pattern P by reporting the k smallest elements in D(locus(P )).This solves the problem in O(n 2 ) space and O(m + k) query time.The main idea in our simple solution is to build a heavy path decomposition of the suffix tree and compactly represent sets on the same path via a reduction to the orthogonal line segment intersection problem while maintaining optimal time queries.This is similar to the data structure by Navarro and Thankachan [34], but our reduction is different.
Heavy path decomposition A heavy path decomposition of a tree T is defined as follows: Starting from the root, at every node, we choose the edge to the child with the largest subtree as heavy edge, until we reach a leaf.Ties are broken arbitrarily.This defines a heavy path, and all edges hanging off the heavy path are light edges.The root of a heavy path h is called the apex of the path, denoted apex(h).We then recursively decompose all subtrees hanging off the path.The heavy path decomposition has the following property: Lemma 2 (Sleator and Tarjan [37]).Given a tree T of size n and a heavy path decomposition of T , any root-to-leaf path in T contains at most O(log n) light edges.
Orthogonal line segment intersection Similarly as Navarro and Thankachan [34], we are going to reduce the problem to a geometric problem on orthogonal line segment intersection.Specifically, we are going to reduce to the following problem: Let L be a set of n vertical line segments in a plane with non-negative x-coordinates.The orthogonal line segment intersection problem is to preprocess L to support the query: • smallest-segments(y0, k): return the first k segments intersecting the horizontal line with y-coordinate y0 in left-to-right order.
We will assume that y0 is an integer, which suffices for our purpose.Let N be the maximum y-coordinate of a segment in L. The following lemma follows easily from the results on partially persistent data structures by Driscoll et al. [17].
Lemma 3. We can solve the line segment intersection problem as described above in O(n + N ) space and O(k) time.
Proof.Consider the x-coordinates as the elements of a set X and the y-coordinate as time.The version of X at a time y0 contains exactly the x-coordinates of the line segments which intersect the horizontal line at y0.Now, the data structure is a partially persistent sorted doubly linked list L on the elements of X.The elements are sorted in increasing order.Since we have at most n line segments, the maximum size of X as well as the maximum number of updates is n.Each update changes only O(1) pointers in the linked list.Using the node copying technique from Driscoll et al. [17] we can build a partially persistent linked list using O(n) space.To be able to find version y0 in constant time, we keep an array of size N with a pointer to the root of the version at each possible time step.For a query (y0, k), use the sorted linked list L to report the k smallest elements at time y0.
If we use a linear scan to find the place to insert an element or find the element to be deleted we get a preprocessing time of O(n 2 ).This can be improved to O(n log n) by using a (non-persistent) balanced binary search tree during the preprocessing holding all elements in the current version of L together with a pointer to their node in the current version.When performing an update the binary search tree is used to find the position where the element must be inserted/deleted in O(log n) time.After the preprocessing step the tree is discarded.

Data Structure
We construct a heavy path decomposition of the suffix tree T of S. Our data structure consists of a line segment data structure from Lemma 3 for each heavy path of T that compactly encodes the sets D(v) for each node v on the path.
We describe the contents of the data structure for a single heavy path h = v1, . . ., v ℓ , where v1 is the apex of the path.Consider a consecutive occurrence (i, j) on some node on h and imagine moving down the heavy path from top to bottom.Either (i, j) is a consecutive occurrence at the apex of h or it will become a consecutive occurrence as soon as every suffix starting at an index between i and j has branched off the heavy path.Then it will stay a consecutive occurrence until either the suffix corresponding to i or the suffix corresponding to j (or both) branch off h.Thus, there exists an interval [d1, d2] of depths on the heavy path such that (i, We encode the consecutive occurrences by line segments in the plane which describe their distance and the interval in which they are alive along the heavy path.Conceptually, the x-coordinate in our coordinate system corresponds to the distance of a consecutive pair, and the y-coordinate corresponds to the depth on the heavy path.Now, for each consecutive occurrence (i, j), we define a vertical line segment with x-coordinate set to its distance, and y-coordinate spanning the interval [d1, d2], where [d1, d2] is the interval in which (i, j) is alive.For an example, see Figure 2. Our data structure for h stores the above line segments in the line segment data structure from Lemma 3.For each line segment in the data structure we store a pointer to the pair of occurrences it represents.The full data structure for T consists of the line segment data structures for all of the heavy paths in T .
Space analysis For a given heavy path h, a leaf in the subtree of apex(h) can be in at most two consecutive occurrences in D(apex(h)).Consider a light edge (v d , u) leaving h at depth d.Any leaf in the subtree rooted at u can be part of at most two consecutive occurrences in D(v d ).A single leaf can thus make at most two consecutive occurrences from D(v d ) disappear in D(v d+1 ) and at most one new consecutive occurrence appear.If we consider all leaves that leave h, we therefore get at most three changes per leaf.Thus, for a given heavy path h a leaf in the subtree of apex(h) can be in at most two consecutive occurrences in D(apex(h)) and can cause at most three changes of line segments in the line segment data structure for h.Since any root-to-leaf path can intersect at most log n heavy paths, any leaf can contribute O(log n) line segments.Overall, this means that there are at most O(n log n) line segments in total.For a single heavy path h the line segment data structure from Lemma 3 uses linear space in the number of segments and the length of h.The sum of the lengths of the heavy paths is O(n), since the heavy paths are disjoint.Thus the total space usage is O(n log n).

Algorithm
Given a pattern P and an integer k we can now answer a query as follows.We begin by finding locus(P ) in the suffix tree.Let h be the heavy path that the locus is on and let dP be the depth of locus(P ) on h.We do a smallest-segments(dP , k) query on the line segment data structure stored for h and report the consecutive occurrences corresponding to the returned line segments.
Correctness By definition, D(locus(P )) contains the consecutive occurrences of P .Thus, every consecutive occurrence of P defines a line segment in the data structure for h and the horizontal line with y-coordinate set to dP intersects exactly those line segments.Since we set the x-coordinate of every line segment to the distance Here, if we have overlapping line segments, we denote by a number how many consecutive occurrences the current segment corresponds to.At depth 1, we have a line segment corresponding to pairs of consecutive occurrences of string Athere are six pairs that have a distance of 2, three pairs that have a distance of 3, two pairs that have a distance of 4, and so on.At depth 2, we encode the consecutive occurrences of string AN.Some of them are the same as for string A.
of its consecutive occurrence, the line segments are sorted left-to-right by increasing distance.Thus, the first k line segments intersecting the horizontal line at y = dP correspond to the top-k close consecutive occurrences.

Time analysis
The time for finding locus(P ) in the suffix tree is O(m).The time for querying the line segment data structure from Lemma 3 is O(k), so the total time complexity is O(m + k).This proves Theorem 1(i).

A Linear Space Solution for Fixed k
In this section, we present a linear space and O(m + k) time solution for the simpler problem where k is known at construction time.That is, given a string S and a positive integer k, we preprocess S into a compact data structure such that given a pattern string P , we can efficiently find the top-k close consecutive occurrences of P in S.This data structure demonstrates one of the key ideas that our final result builds on.
The main idea behind the data structure is to store the line segment solution from Section 3 for some path segments of the suffix tree, such that all nodes that are not on these paths are within small subtrees.For nodes within such small subtrees we can find all consecutive occurrences without spending too much time.Specifically, we will partition the suffix tree into clusters, satisfying some properties.We are going to define this cluster partition next.

Cluster Partition
For a connected subgraph C ⊆ T , a boundary node v is a node v ∈ C such that either v is the root of T , or v has an edge leaving C -that is, there exists an edge (v, u) in the tree T such that u ∈ T \ C. A cluster is a connected subgraph C of T with at most two boundary nodes.A cluster with one boundary node is called a leaf cluster.A cluster with two boundary nodes is called a path cluster.For a path cluster C, the two boundary nodes are connected by a unique path.We call this path the spine of C. A cluster partition is a partition of T into clusters, i.e. a set CP of clusters such that C∈CP V (C) = V (T ) and C∈CP E(C) = E(T ) and no two clusters in CP share any edges.Here, E(G) and V (G) denote the edge and vertex set of a (sub)graph G, respectively.We need

Data Structure
For the suffix tree of S, we build a clustering as in Lemma 4 with parameter τ set to k to get O(n/k) clusters of size at most k.For the spine of every path cluster, we build a line segment data structure similar to the one from Section 3. The difference is that for any depth, we only maintain the line segments that correspond to the top-k close consecutive occurrences for that depth.Let v1, . . ., v l denote the nodes on the spine, starting at the top boundary node.Note that for any consecutive occurrence that appears for the first time in D(v d+1 ) there is a consecutive occurrence in D(v d ) of smaller distance which is no longer present in D(v d+1 ).It follows that, when moving down the spine, once a consecutive occurrence (i, j) is amongst the k closest, it will stay amongst the k closest until suffix i or j branches off the spine.Thus, there exists an interval [d1, d2] of consecutive depths such that (i, j) is amongst the k closest pairs in D(v d ) if and only if d ∈ [d1, d2].For a consecutive occurrence (i, j) that is amongst the k closest for any v on the spine, we define a line segment where the x-coordinate is its distance and the y-coordinate is spanning the interval [d1, d2], where [d1, d2] is the interval in which (i, j) is amongst the k closest pairs.For these line segments we store the data structure from Lemma 3. Again, for each line segment we store the pair of occurrences it represents.We store this data structure for the spine of each cluster and for every node that is on that spine we store a pointer to the data structure.For boundary nodes that are on multiple spines we store a pointer to any one of them.See Figure 3 for an illustration of this structure.Additionally we store the suffix array and the sorted range reporting data structure of Brodal et al. [13] on the suffix array.

Space analysis
We show that for every path cluster there are O(k) line segments: We still have the property that a line segment only ends if a corresponding leaf branches off the spine.In that case, it might be replaced either by a new consecutive occurrence or by a consecutive occurrence that was there before but was not amongst the k closest.Note that at any node on the spine except the boundary nodes, any subtrees branching off the spine are fully contained within the cluster, and as such have total size at most k.Between the top boundary node and the next node on the spine, we have no bound as to how many leaves can branch off -however, since we only store line segments corresponding to the top-k consecutive occurrences, at most k line segments can be replaced by k other line segments.For the rest of the spine, at most k leaves can branch off in total.Every leaf that branches off can cause at most two line segments to end and two new line segments to begin.As such there can be at most O(k) line segments.As the size of the line segment data structure is linear in the number of line segments and in the length of the spine, any line segment data structure of a path cluster uses O(k) space.As both the sorted range reporting data structure and the suffix array have linear space complexity, the complete data structure occupies O((n/k)k + n) = O(n) space.

Algorithm
Given a pattern P we can now answer the top-k query.We begin by finding locus(P ) in the suffix tree.If the locus is on a spine, we query the line segment data structure for that spine.Otherwise the locus is either in a subtree hanging off a spine or in a leaf cluster.In both cases, there are at most k occurrences of our pattern P .We find all occurrences of P in text order, using the sorted range reporting data structure.This allows us to report the consecutive occurrences: Let i1, ..., i l denote the leaves in text order, then the consecutive occurrences are (i1, i2), (i2, i3), ...(i l−1 , i l ).Note that l ≤ k, since the size of the subtree is at most k.
Correctness By construction, for any depth on a spine, the top-k close consecutive occurrences of the corresponding substring will have corresponding line segments present at that depth in the line segment data structure.If the locus is on a spine, then by the arguments in Section 3, the line segment data structure will report the top-k close consecutive occurrences.If the locus is not on a spine, then there are at most k occurrences of P in total, since any subtree hanging off a spine and any leaf cluster has at most k leaves.Thus, by constructing and reporting all consecutive occurrences of P we report the top-k close consecutive occurrences.We are going to use this data structure with different parameters in Section 5.For a general parameter τ , we have the following lemma: Lemma 5.For any positive integer τ , there exists a cluster partition of the suffix tree and a linear space data structure with the following properties:

Time analysis
1.For any k ≤ τ and P such that locus(P ) is on the spine of a cluster, we can report the top-k close consecutive occurrences in O(m + k) time.
2. For any P such that locus(P ) is not on a spine, we can report the top-k close consecutive occurrences in O(m + τ ) time.
Proof.We build the data structure described in this section for parameter τ taking the role of k.In case 1, we query the line segment data structure for the depth of locus(P ) on the path and k.Since k ≤ τ this will correctly output the top-k close consecutive occurrences of P .In case 2, we have shown that we can construct the top-τ close consecutive occurrences.Using the linear time selection algorithm by Blum et al. [12] we can find the top-k of those: We use the algorithm to find the consecutive occurrence of k th smallest distance d; then we traverse all the consecutive occurrences and output those of distance ≤ d.If needed, we crop the output to report no more than k consecutive pairs.

An O(n log log n) Space Solution for General k
We now show how to leverage the solution from Lemma 5 to obtain a data structure that can answer queries for any k.The idea is to recursively cluster the suffix tree, such that we always either land on a spine with a sufficient number of consecutive occurrences stored, or in a sufficiently small subtree.

Data Structure
Our data structure consists of the suffix tree decomposed into clusters of decreasing size, with the line segment data structure stored for every spine as before.We build it in the following way.First we build the solution from Lemma 5 with parameter τ1 = √ n, resulting in clusters of size at most √ n.For every subtree hanging off a spine and every leaf cluster, we apply the solution with parameter τ2 = √ τ1.We keep recursively applying the solution with parameter τi = √ τi−1 until reaching a constant cluster size.For notational convenience, additionally define τ0 = n.See Figure 4 for an illustration of this data structure.Again we additionally store the suffix array and the sorted range reporting data structure of Brodal et al. [13] on the suffix array.

Space analysis
The suffix array and sorted range reporting structure occupy O(n) space.For a tree of size ñ and any τ , the data structure from Lemma 5 uses at most O(ñ) space.Since at every recursion level, we build the data structure from Lemma 5 on non-overlapping subtrees of the suffix tree, every recursion level uses at most O(n) space.As the cluster size at every level of recursion is the square root of the previous cluster size, there are at most O(log log n) levels.The complete data structure thus uses O(n log log n) space.

Algorithm
Given a query with pattern P and parameter k, we can now answer in the following way.As before, we begin by finding the locus of the pattern in the suffix tree.This node is now either on the spine of some cluster or in a cluster of constant size.If it is on the spine of a cluster of size τi, and if k ≤ τi, then we query the line segment data structure for that spine, which allows us to report the top-k close consecutive occurrences.Otherwise, we find all occurrences of P and construct the top-k close consecutive occurrences by using linear time selection as in the proof of Lemma 5.

Correctness
The correctness of the algorithm follows by the same arguments as previous sections.

Time analysis
Finding the locus in the suffix tree takes O(m) time.The locus is either on the spine of a cluster, or within a cluster of constant size.In a constant sized cluster, clearly we can do all operations described above in constant time.If the locus is on the spine of a cluster with parameter τi, and k ≤ τi, then we are in case 1 of Lemma 5 with τ = τi and can report the top-k close consecutive occurrences using a total of O(m + k) time.If k > τi, then we are in case 2 of Lemma 5 with τ = τi−1.Note that τi−1 = τ 2 i < k 2 .Therefore, we can find the top-k close consecutive occurrences in O(m + τi−1) = O(m + k 2 ) time.In total, the worst case query time is then O(m + k 2 ).In summary, this gives the following result: Lemma 6.Given a string S of length n, we can build a data structure that can answer top-k close consecutive occurrences queries using O(n log log n) space and O(m + k 2 ) query time.Here, m is the length of the query pattern.

A Linear Space Solution
We now show how to reduce the space consumption of the solution presented in Section 5. Observe that in any cluster of level i, we only have O(τi) objects.If we can reduce all objects within a cluster to a "universe size" of O(τi) instead of O(n), we can use O(τi log τi) bits instead of O(τi log n) bits per cluster.In the following, consider a cluster C of level i.
Reducing the line segment data structure In the line segment data structure for cluster C, by the analysis of previous sections, there are at most O(τi) line segments and τi different depths on the path.Let c be a constant such that there are at most cτi line segments for each cluster.We map every unique x-coordinate of a line segment to a unique element in {1, . . ., cτi} in a way that preserves order.That is, map the minimum x-coordinate to 1, the smallest x-coordinate that is bigger than the minimum to 2, and so on.This gives us a modified line segment data structure that preserves the properties we need but is restricted to a cτi × τi grid.
Reducing the leaf pointers For any line segment, we have to store pointers that allow us to report the corresponding pair of consecutive occurrences.Doing so naively uses 2 log n bits per line segment.In the following, we show how to reduce that to 4 log τi, for a cluster C of level i.The idea is to store the offset within the suffix array range defined by the top boundary node r of C.More precisely, let [ar, br] be the range in the suffix array spanning the leaves below r.Then for any leaf l in the subtree rooted at r define off(l) = SA −1 (l) − ar.By the way our recursion is defined, C is fully contained in a subtree of size at most τ 2 i , and thus r has at most τ 2 i leaves below it.It follows that for any leaf l in the subtree of r, off(l) is a number between in [0, τ 2 i − 1] and can be stored using 2⌈log τi⌉ bits.

Data Structure
Our data structure is now defined as follows: We have a clustering of the suffix tree as in Section 5.For every spine on level i, we store the line segment data structure reduced to a cτi × τi grid.Every line segment corresponding to a pair (i, j) stores the pair (off(i), off(j)) as additional information.For every node on the spine, we store a pointer to the spine data structure and to the top boundary node of the spine.Additionally, we store the suffix array and the sorted range reporting structure, as well as two integers for every node in the suffix tree, that define the range of leaves below the node in the suffix array.

Space analysis
The suffix array and the sorted range reporting data structure use space O(n).Storing the range in the suffix array plus at most two pointers per node uses O(n) space.For a cluster C of level i, we store the line segment data structure from Lemma 3 for a cτi × τi grid.Since the data structure from Lemma 3 works in the word RAM model (as do all data structures presented in this paper), we can store the data structure using O(τi log τi) bits.For each of the at most cτi line segments we store 4 log τi bits for the encoding of the consecutive pair.Thus, we can store the data structure for cluster C using O(τi log τi) bits.As in the previous section, at every recursion level, we cluster non-overlapping subtrees.The reduced cluster solution of a subtree of size ñ with parameter τi uses O( ñ τ i τi log τi) = O(ñ log τi) bits.The total space for all clusters of level i thus becomes O(n log τi).Summing over all recursion levels, we get

Algorithm
We query the data structure as follows: If we land on a spine and k ≤ τi, we query the line segment data structure and get k pairs of the form (off(i), off(j)).We then use the pointer to get to the root of the spine and use the range in the suffix array to translate each encoding back to the original suffix number, using constant time per leaf.Otherwise, we proceed as described in Section 5. Since the decoding can be done in constant time per leaf, the time complexities are the same as in Section 5. We have shown the following result: Lemma 7. Given a string S of length n, we can build a data structure that can answer top-k close consecutive occurrences queries using O(n) space and O(m + k 2 ) query time.Here, m is the length of the query pattern.
In order to get Theorem 1(ii), we cluster according to a parameter ϵ, 0 < ϵ ≤ 1, using the following recursion: τ0 = n and τi = τ Hence, the total space in bits is now: that is, O n ϵ log n bits, so O n ϵ words.For the query time, there are again two cases.In the case where locus(P ) is on a spine with k ≤ τi, we get optimal O(m + k) time, as before.For the other case, we have at most τi−1 = τ 1+ϵ i < k 1+ϵ occurrences of P , which gives us a time complexity of O(m + k 1+ϵ ).This concludes the proof of Theorem 1(ii).

A Different Tradeoff
In this section we give a solution query time O(m + k log 1+ϵ n).The idea is to store a finer set of cluster decompositions than in the previous section and store sublinear information for each cluster decomposition.Then we use a bounded number of orthogonal range successor queries in each cluster.
Orthogonal range successor The orthogonal range successor problem is to preprocess an array A[0, . . ., n− 1] into a data structure that efficiently supports the following queries: • RangeSuccessor(a, b, x): return the successor of x in A[a, . . ., b], that is, the minimum y > x such that there • RangePredecessor(a, b, x): return the predecessor of x in A[a, . . ., b], that is, the maximum y < x such that there is an Nekrich and Navarro [35] give a linear space data structure such that each range successor query takes O(log ϵ n) time.We will use a range successor data structure on the suffix array to answer the following type of queries: Given an index i and the suffix array range of a pattern P , find the next position in the text after i where P occurs.

Data Structure
We store the linear space range successor data structure from Nekrich and Navarro [35] and the sorted range reporting data structure by Brodal et al. [13] on the suffix array of S. Further, we store the suffix tree of S together with the following cluster decompositions.For each κ = 2, 4, 8, 16, . . ., 2 ⌊log n⌋ we build the clustering decomposition of Lemma 4 of the suffix tree for cluster size τ = κ log n.For each boundary node v, we store the top-κ close consecutive occurrences of str(v), sorted by text position.For each node v in the suffix tree, we additionally store two bit vectors of length log n.The first one is used to store for which κ node v is a boundary node, that is, the ith bit is set to 1 if v is a boundary node in the cluster decomposition with κ = 2 i and 0 otherwise.Similarly, the other bit vector stores for which κ node v is on a spine in the cluster decomposition with κ = 2 i .

Space analysis
The suffix array, the range successor data structure and the sorted range reporting data structure all uses O(n) space.The suffix tree together with the bit vectors saved in the nodes also uses O(n) space.By Lemma 4, there are O(n/(κ log n) boundary nodes in the cluster decomposition with cluster size κ log n.
For each boundary node we store κ values and thus the space used for a fixed There are O(log n) different values of κ and therefore the total space is O(n).

Algorithm
Given a pattern P and parameter k we can now answer the top-k query in the following way.As before, we begin by finding the locus of P in the suffix tree.Then we find the smallest power of two bigger than k, i.e. κ = 2 ⌈log k⌉ , and consider the cluster decomposition defined for κ.There are two cases depending on whether locus(P ) is on a spine in the cluster decomposition for κ or not.If locus(P ) is not on a spine, then as in Lemma 5 (2), there are at most τ = κ log n leaves below locus(P ), and we can construct the top-k close occurrences in time O(κ log n).If locus(P ) is on a spine, then we find the lower boundary node b of the cluster (note we can do that by traversing the suffix tree and checking at most O(κ log n) nodes).We have the following property.
Claim 8.Each of the top-k close consecutive occurrences of P is either 1. stored at b or 2. includes an occurrence of P corresponding to a leaf within the cluster.
Proof.Any top-k consecutive occurrence of P , where both occurrences are below b, is also among the top-k consecutive occurrences of str(b).This is true because P is a prefix of str(b), so the occurrences of str(b) is a subset of the occurrences of P .Thus any consecutive occurrence of P where both occurrences are below b is also a consecutive occurrence of str(b).A consecutive occurrences of str(b) that is not consecutive occurrences of P must be split by an occurrence of P that is not below b giving rise to a least two closer consecutive occurrences of P .Thus the i-closest occurrence of str(b) must have distance at least the same as the i-closest occurrence of P .Every occurrence of P that is not below b is within the cluster.We find all occurrences of P that are within the cluster in text order using the sorted range reporting data structure.We can do this using two calls to the sorted range reporting data structure since the occurrences of P within the cluster correspond to two intervals in the suffix array, namely the range of locus(P ) minus the range of b.For each such occurrence i, we use an orthogonal range successor query j = RangeSuccessor(range(locus(P )), i) to find the next occurrence j and then an orthogonal predecessor query i ′ = RangePredecessor(range(locus(P )), j) to find the last occurrence i ′ before j.This gives us a consecutive occurrence (i ′ , j).To avoid recomputing the same consecutive occurrence we skip through the list until we get to an occurrence that is after i ′ .Now we have the sorted list of the top-κ consecutive occurrences of str(b) stored at b, and a sorted list of all consecutive occurrences of P that include an occurrence corresponding to a leaf within the cluster.By Claim 8, any of the top-k consecutive occurrences of P is part of one of these lists.However, some of the consecutive occurrences of str(b) might not be consecutive occurrences of P , since P might have extra occurrences inbetween.Let (i, j) be a consecutive occurrence of str(b), and let i ′ be the last occurrence of P before j.If i ′ ̸ = i then i ′ is wihtin the cluster and thus (i ′ , j) is part of the consecutive occurrences we already computed.We merge the two lists, deleting all consecutive occurrences of str(b) that are not consecutive occurrences of P and all duplicates.We then find the top-k of remaining consecutive occurrences, by using the linear time selection algorithm as in the proof of Lemma 5.

Time analysis
Finding the locus in the suffix tree takes O(m) time.Finding b and takes O(κ log n) time, and finding the occurrences of P within the cluster using sorted range reporting also takes O(κ log n) time.We make O(κ log n) calls to the orthogonal range reporting data structure each using O(log ϵ n) time.In total the time for this is O(κ log 1+ϵ n).Merging the two lists and using the selection algorithm takes time linear in the total length of the two lists which is O(κ log n).Since κ < 2k the total time complexity O(m + k log 1+ϵ n).This concludes the proof of the main result.

Extensions
Our results can be extended to a couple of related problems.In Section 8.1, we show how we can modify our data structure to solve the "opposite" problem of reporting the k consecutive occurrences of largest distance.The extension is quite natural, though it does require some careful analysis.In Section 8.2, we then relate the solutions from Section 8.1 and the solutions to Sitcco to the problem of finding consecutive occurrences with distances in a specified interval, considered by Navarro and Thankachan [34].We show improved complexities for the special case where one of the interval bounds is known at indexing time.Finally, we show how to use those results to efficiently find all pairs of non-overlapping consecutive occurrences.

Top-k Far Consecutive Occurrences
Given a pattern P and an integer parameter k > 0, define the top-k far consecutive occurrences of P to be the k consecutive occurrences of P in S with the largest distances.Given a string S the string indexing for top-k far consecutive occurrences problem (Sitfco) is to preprocess S into a data structure that supports top-k far consecutive occurrences queries.The goal is to obtain a compact data structure while supporting fast queries in terms of the length of the pattern P and the number of reported occurrences k.
Line segments and an O(n log n) space solution We can solve the Sitfco problem using the same strategy as for the Sitcco problem, with small modifications.We need a similar data structure from Lemma 3 to report the line segments with largest x-coordinates.As previously, assume we are given a set L of n vertical line segments.We need a data structure for the following problem: • largest-segments(y0, k): return the first k segments intersecting the horizontal line with y-coordinate y0 in right-to-left order.
As before, we can assume integer coordinates and let N be the maximum y-coordinate of any line segment in L. Proof.We build the same data structure as in Lemma 3, but keep the partially persistent linked list sorted in decreasing order.The rest follows as before.Now, using this data structure in the solution described in Section 3, we immediately get an analogous result for Sitfco: Lemma 10.Given a string S of length n, we can build a data structure that can answer top-k far consecutive occurrences queries using O(n log n) space and O(m + k) query time.
Modifications to the linear space data structure Now we extend the cluster solutions from Sections 5 and 6.We build the same recursive clusters as in Section 5.For each spine of a cluster of size τi, we keep the line segments corresponding to the τi consecutive occurrences of largest distance at every depth on the spine.That is, if a consecutive occurrence (i, j) is among the k farthest within D(v d ) for some v d on the spine, define line segments for all maximal consecutive intervals [d1, d2] such that (i, j) is amongst the k farthest within D(v d ) for any d ∈ [d1, d2].Again, the x-coordinate of the line segment is the distance j − i, and the y-coordinate spans [d1, d2].Note that in this case, a consecutive occurrence might define more than one line segment.See Figure 5 for an illustration of pair defining more than one line segment.We store these line segments in the data structure from Lemma 9.
Space analysis When moving down a spine from v d−1 to v d , only three different types of changes can happen to the set of the k farthest consecutive occurrences.We again denote D(v d ) to be the set of all consecutive occurrences of str(v d ).The possibles types of changes are then as follows.
• A consecutive occurrence can be removed from the k farthest because a consecutive occurrence of larger distance is added to D(v d ).The consecutive occurrence of larger distance can only appear if an occurrence in between branched off.This leaf accounts for this change.A leaf can account for at most one such change, which triggers a line segment ending and a new line segment appearing at depth d.
• A consecutive occurrence (i, j) can disappear because either i or j branched off.Then this leaf accounts for this change.A leaf can account for at most two such changes.
• A consecutive occurrence that was present in D(v d−1 ) but not amongst the k farthest can be added to the k farthest in D(v d ).This can only happen if a consecutive occurrence of greater distance disappeared because one or both of its occurrences branched off.Then this leaf accounts for this new line segment also, additional to the charge of the disappearing consecutive occurrence(s).A leaf can account for at most two such changes.
In total, any leaf can account for at most a constant number of changes.Thus, we get the same space complexities as in Section 5.
Algorithm To answer a query we proceed as in Section 5. We first find locus(P ).If it is on a spine of a cluster of size O(τi) and k < τi, we query the line segment data structure to report the top-k far consecutive occurrences.Otherwise, we find all occurrences of P in text order, construct the consecutive occurrences and use linear time selection to output the k consecutive occurrences of largest distance.This is correct by the same arguments as Section 5, and by similar arguments, achieves the same time complexities.The rank space reduction from Section 6 can be applied analogously.This gives us the following result: Theorem 11.Given a string S of length n and ϵ, 0 < ϵ ≤ 1, we can build a data structure that can answer top-k far consecutive occurrences queries using either Remark We note that the construction from Section 7 does not generalize to the top-k far consecutive occurrences problem, since the corresponding version of Claim 8 does not hold.A top-k far consecutive occurrence of P , where both occurrences are below b is not necessarily among the top-k far consecutive occurrences of str(b).A consecutive occurrence of str(b) can be split by an occurrence of P not below b.This gives two smaller occurrences, and might cause the (k + 1)th furthest occurrence below b to be among the top-k far occurrences of P .Each occurrence of P from within the cluster can split a consecutive occurrence below b, and thus we would need to store Θ(τ ) occurrences below b, which no longer gives a linear space solution.

Consecutive Occurrences with Gaps
Given a string S the string indexing for consecutive occurrences with gaps problem (Sicog) is to preprocess S into a compact data structure, such that for any pattern P and a range [α, β] we can efficiently find all consecutive occurrences of P where the distance lies within [α, β].The Sicog problem was considered by Navarro and Thankachan [34] and they give an O(n log n) space and O(m + occ) time solution, where occ is the number of consecutive pairs with distance in [α, β].Using the data structure from Section 3, we get an O(n log n) space and O(m + log n + occ) time solution for the Sicog problem, which can be optimized using the same strategy as in [34] to achieve the same complexities.However, for a special case of the problem where either α or β is known at indexing time we can get a similar trade-off as for the Sitcco problem: We first the describe our solution for the fixed-α variant using the techniques from Sections 5 and 6, and then the fixed-β variant follows by applying the same ideas combined with the data structure from Section 8.1.

Data structure
We build the same data structure as in Section 5, with a slight modification.In the line segment data structure stored at every spine, instead of storing the τi closest pairs, we store the τi closest pairs that have distance ≥ α.This clearly occupies no more space than the solution from Section 5 and we can still apply the space optimizations of Section 6.
Algorithm Given P and β, we can now answer a query as follows.We begin by finding locus(P ).If it is in a subtree of constant size, we construct all the consecutive occurrences of P and report those that have distance within [α, β].If it is on a spine of a cluster of size τi, we query the line segment data structure.For every consecutive occurrence we find, we check if the distance is ≤ β.If we encounter a pair with distance > β, we stop reporting.If all τi consecutive occurrences at locus(P ) have distance ≤ β, we then find all the the consecutive occurrences of P , just as in Section 5, and scan them once to report all the consecutive occurrences with distance in [α, β].
For the analysis, define a relevant pair to be a consecutive occurrence with a distance in [α, β].If there are less than τi relevant pairs for any locus, then they will all be stored and reported by the line segment data structure.As they are stored in order of increasing distance, once we reach a pair with distance > β, no further relevant pairs exist.If there are more than τi relevant pairs, then we consider all occurrences of the pattern and report from those.Thus we always answer the query correctly.
If there is a consecutive occurrence among the τi line segments with distance > β, we spend time O(m + occ) finding the locus and querying the line segment data structure.Otherwise we have that occ ≥ τi, and thus occ 1+ϵ ≥ τi−1.As before, P has at most τi−1 occurrences.Therefore, by the same arguments as in the previous section, we get the following result: Theorem 12.Given a string S of length n and α > 0, we can build for any ϵ satisfying 0 < ϵ ≤ 1 an O( n ϵ ) space data structure that can answer the following query in O(m + occ 1+ϵ ) time: For a query pattern P and β ≥ α, report all consecutive occurrences of P in S where the distance lies in [α, β].Here, m is the length of the pattern and occ is the number of reported occurrences.
By combining the same arguments with the solution for top-k far consecutive occurrences, we get the following result for β fixed at indexing time: Theorem 13.Given a string S of length n and β > 0, we can build for any ϵ satisfying 0 < ϵ ≤ 1 an O( n ϵ ) space data structure that can answer the following query in O(m + occ 1+ϵ ) time: For a query pattern P and α where 0 < α ≤ β, report all consecutive occurrences of P in S where the distance lies in [α, β].Here, m is the length of the pattern and occ is the number of reported occurrences.
We note that the construction from Section 7 does not generalize to the problem where α is fixed, as the corresponding version of Claim 8 do not hold in this case.A consecutive occurrence below b of distance at least α can be split by an occurrence of P from within the cluster introducing two new consecutive occurrences that might both have distance less than α.
We can, however, get a solution to the problem when α = 1.Using the data structure from Section 7, we can get all consecutive occurrences that are at most β apart as follows.We query the data structure for the top-k close consecutive occurrences with k = 1, 2, 4, . .., each time checking if all the top-k close consecutive occurrences have distance at most β.As soon as we find a consecutive occurrence that has a distance more than β among our top-k close consecutive occurrences, we stop and report all the occurrences found in this last call that have distance at most β.This way we ensure that k ≤ 2 • occ.We only find the locus once.The total query time is O(m + ⌈log occ⌉ i=0 2 i log 1+ϵ n) = O(m + occ log 1+ϵ n).Lemma 14.Given a string S of length n we can build an O(n) space data structure that can answer the following query in O(m + occ log 1+ϵ n) time: For a query pattern P and β > 0, report all consecutive occurrences with distance at most β.
Non-overlapping consecutive occurrences A natural and well-studied variant of string indexing is the problem of finding sets of non-overlapping occurrences of a pattern P .Here, a set of non-overlapping occurrences is a set of occurrences {i1, . . ., i k } of P such that the distance between any two of them is at least |P |.Several papers study the problem of finding the set of non-overlapping occurrences of maximum size [16,20,25,27].Note that Theorem 13 applied to α = |P | solves a different variant of finding sets of non-overlapping occurrences: Namely, finding all pairs of non-overlapping consecutive occurrences.We call this problem the string indexing for non-overlapping consecutive occurrences problem (Sinoco) .The Sinoco problem is inherently different from finding the maximum set of non-overlapping occurrences: For example, the maximum set of non-overlapping occurrences of the pattern P = NANA in the string S = NANANANA has size 2.However, there are no nonoverlapping consecutive occurrences.To the best of our knowledge, the Sinoco problem has not been studied before.An immediate corollary of the results in Navarro and Thankachan [34] and Theorem 13 gives the following trade-offs for solving Sinoco: Corollary 15.Given a string S of length n and ϵ, 0 < ϵ ≤ 1, we can build a data structure that can find all non-overlapping consecutive occurrences of a query pattern P using either Proof.Apply the results in [34] and Theorem 13 with β = n and α = |P |.

Conclusion and Open Problems
We have introduced the natural problem of string indexing for top-k close consecutive occurrences, and have given both a near-linear space solution achieving optimal query time and a linear space solution achieving a query time that is close to optimal.Using these techniques, we have given new solutions for the problem of string indexing for consecutive occurrences with gaps (Sicog).Furthermore, we have introduced the problem of finding all non-overlapping consecutive occurrences of a pattern (Sinoco) and showed that it can be reduced to a special case of Sicog.
These results open interesting new directions for further research.The most obvious open problem is to see whether it is possible to further improve the results for the main problem considered in this paper, especially, achieve linear space and optimal query time simultaneously.Secondly, it is still open whether it is possible to get an O(m + occ) time and linear space solution for the special case of the Sicog problem where one of the interval endpoints is fixed, or even o(n log n) space for the general problem.For the Sinoco problem, one might find better solutions that do not reduce it to Sicog but use additional insights about the specific structure of the problem.
(i) O(n log n) space and O(m + k) query time or (ii) O( n ϵ ) space and O(m + k 1+ϵ ) query time.(iii) O( n ϵ ) space and O(m + k log 1+ϵ n) query time.
Figure 2: Line segments for a heavy path from the suffix tree for "BATMAN-AND-ANNA-SING-NANANANA-AND-EAT-BANANAS".Here, if we have overlapping line segments, we denote by a number how many consecutive occurrences the current segment corresponds to.At depth 1, we have a line segment corresponding to pairs of consecutive occurrences of string Athere are six pairs that have a distance of 2, three pairs that have a distance of 3, two pairs that have a distance of 4, and so on.At depth 2, we encode the consecutive occurrences of string AN.Some of them are the same as for string A.

Figure 3 :
Figure3: The suffix tree is divided into clusters (grey loops) of size ≤ k which are either leaf clusters, or path clusters with spines marked in red.For every spine we store a line segment data structure, also marked in red.

Lemma 4 .
Given a tree T with n nodes and a parameter τ , there exists a cluster partition CP such that |CP | = O(n/τ ) and every C ∈ CP has at most τ nodes.Furthermore, such a partition can be computed in O(n) time.
We find the locus in O(m) time.If we land on a spine we report in O(k) time.Otherwise, we are in a subtree of size at most O(k) and thus P has at most k occurrences.Using sorted range reporting we can find the occurrences in text order using O(k) time.The total time for a query is thus O(m + k).

Figure 4 :
Figure 4: Here, we see the recursive clustering: The black clustering is the coarsest clustering and the green and blue are finer sub-clusterings.

Figure 5 :Lemma 9 .
Figure 5: Illustration of a pair defining more than one line segment.To the left are the positions of the occurrences in S, in the middle is the spine of a cluster and to the right are the corresponding line segments.The pair (i, j) is amongst the to k farthest until the occurrence x disappears, after which it is pushed out by the pair (a, b).When b then disappears, (i, j) is again amongst the k farthest.
(i) O(n log n) space and O(m + k) query time or (ii) O( n ϵ ) space and O(m + k 1+ϵ ) query time.Here, m is the length of the query pattern.
(i) O(n log n) space and O(m + occ) query time or (ii) O( n ϵ ) space and O(m + occ 1+ϵ ) query time.Here, m is the length of the query pattern and occ is the number of reported occurrences.