String Indexing for Patterns with Wildcards††thanks: Preliminary version appeared in Proceedings of the 13th Scandinavian Symposium and Workshops on Algorithm Theory. Lecture Notes in Computer Science, vol. 7357, pp. 283–294, Springer 2012.
We consider the problem of indexing a string of length to report the occurrences of a query pattern containing characters and wildcards. Let be the number of occurrences of in , and the size of the alphabet. We obtain the following results.
A linear space index with query time . This significantly improves the previously best known linear space index by Lam et al. [ISAAC 2007], which requires query time in the worst case.
An index with query time using space , where is the maximum number of wildcards allowed in the pattern. This is the first non-trivial bound with this query time.
A time-space trade-off, generalizing the index by Cole et al. [STOC 2004].
We also show that these indexes can be generalized to allow variable length gaps in the pattern. Our results are obtained using a novel combination of well-known and new techniques, which could be of independent interest.
The string indexing problem is to build an index for a string such that the occurrences of a query pattern can be reported. The classic suffix tree data structure  combined with perfect hashing  gives a linear space solution for string indexing with optimal query time, i.e., an space data structure that supports queries in time, where is the number of occurrences of in .
Recently, various extensions of the classic string indexing problem that allow errors or wildcards (also known as gaps or don’t cares) have been studied [11, 24, 37, 36, 6, 28, 32]. In this paper, we focus on one of the most basic of these extensions, namely, string indexing for patterns with wildcards. In this problem, only the pattern contains wildcards, and the goal is to report all occurrences of in , where a wildcard is allowed to match any character in .
String indexing for patterns with wildcards finds several natural applications in large-scale data processing areas such as information retrieval, bioinformatics, data mining, and internet traffic analysis. For instance in bioinformatics, the PROSITE data base [21, 5] supports searching for protein patterns containing wildcards.
Despite significant interest in the problem and its many variations, most of the basic questions remain unsolved. We introduce three new indexes and obtain several new bounds for string indexing with wildcards in the pattern. If the index can handle patterns containing an unbounded number of wildcards, we call it an unbounded wildcard index, otherwise we refer to the index as a -bounded wildcard index, where is the maximum number of wildcards allowed in . Let be the length of the indexed string , and be the size of the alphabet. We define and to be the number of characters and wildcards in , respectively. Consequently, the length of is . We show that,
There is an unbounded wildcard index with query time using linear space. This significantly improves the previously best known linear space index by Lam et al. , which requires query time in the worst case. Compared to the index by Cole et al.  having the same query time, we improve the space usage by a factor .
There is a -bounded wildcard index with query time using space . This is the first non-trivial space bound with this query time.
There is a time-space trade-off for -bounded wildcard indexes. This trade-off generalizes the index described by Cole et al. .
Furthermore, we generalize these indexes to support variable length gaps in the pattern.
1.1 Previous Work
Exact string matching has been generalized with error bounds in many different ways. In particular, allowing matches within a bounded hamming or edit distance, known as approximate string matching, has been subject to much research [25, 26, 35, 12, 10, 37, 6, 28, 11, 32, 19, 2]. Another generalization was suggested by Fischer and Paterson , allowing wildcards in the text or pattern.
Work on the wildcard problem has mostly focused on the non-indexing variant, where the string is not preprocessed in advance [14, 13, 9, 23, 8, 4]. Some solutions to the indexing problem consider the case where wildcards appear only in the indexed string  or in both the string and the pattern [11, 24].
In the following, we summarize the known indexes that support wildcards in the pattern only. We focus on the case where , since for the problem is classic string indexing. For , Cole et al.  describe a selection of specialized solutions. However, these solutions do not generalize to larger .
Several simple solutions to the problem exist for . Using a suffix tree for , we can find all occurrences of in a top-down traversal starting from the root. When we reach a wildcard character in in location , the search branches out, consuming the first character on all outgoing edges from . This gives an unbounded wildcard index using space with query time , where is the total number of occurrences of in . Alternatively, we can build a compressed trie storing all possible modifications of all suffixes of containing at most wildcards. This gives a -bounded wildcard index using space with query time .
In 2004, Cole et al.  gave an elegant -bounded wildcard index using space and with query time. For sufficiently small values of this significantly improves the previous bounds. The key components in this solution are a new data structure for longest common prefix (LCP) queries and a heavy path decomposition  of the suffix tree for the text . Given a pattern , the LCP data structure supports efficient insertion of all suffixes of into the suffix tree for , such that subsequent longest common prefix queries between any pair of suffixes from and can be answered in time. This is where the term in the query time comes from. The heavy path decomposition partitions the suffix tree into disjoint heavy paths such that any root-to-leaf path contains at most a logarithmic number of heavy paths. Cole et al.  show how to reduce the size of the index by only creating additional wildcard tries for the off-path subtries. This leads to the space bound. Secondly, using the new tries, the top-down search branches at most twice for each wildcard, leading to the term in the query time. Though Cole et al.  did not consider unbounded wildcard indexes, the technique can be extended to this case by using only the LCP data structure and omitting the additional wildcard tries. This leads to an unbounded wildcard index with query time using space .
The solutions described by Cole et al.  all have bounds which are exponential in the number of wildcards in the pattern. Very recently, Lewenstein  used similar techniques to improve the bounds to be exponential in the number of gaps in the pattern (a gap is a maximal substring of consecutive wildcards). Assuming that the pattern contains at most gaps each of size at most , Lewenstein obtains a bounded index with query time using space , where is the number of gaps in the pattern.
A different approach was taken by Iliopoulos and Rahman , who describe an unbounded wildcard index using linear space. For a pattern consisting of strings (subpatterns) interleaved by wildcards, the query time of the index is , where denotes the number of matches of in . This was later improved by Lam et al.  with an index that determines complete matches by first identifying potential matches of the subpatterns in and subsequently verifying each possible match for validity using interval stabbing on the subpatterns. Their solution is an unbounded wildcard index with query time using linear space. However, both of these solutions have a worst case query time of , since there may be matches for a subpattern, but no matches of . Table 1 summarizes the existing solutions for the problem in relation to our results.
|Unbounded||Iliopoulos and Rahman|||
|Lam et al.|||
|Simple suffix tree index|
|Cole et al.|||
|-bounded||Heavy -tree decomposition|
|Cole et al.|||
|Special index for|
|Simple linear time index|
The unbounded wildcard index by Iliopoulos and Rahman  was the first index to achieve query time linear in while using space. Recently, Chan et al.  considered the related problem of obtaining a -mismatch index supporting queries in time linear in and using space. They describe an index with a query time of . However, this bound assumes a constant-size alphabet and a constant number of errors. In this paper we make no assumptions on the size of these parameters.
1.2 Our Results
Our main contribution is three new wildcard indexes.
Let be a string of length from an alphabet of size . There is an unbounded wildcard index for using space. The index can report the occurrences of a pattern with characters and wildcards in time .
Compared to the solution by Cole et al. , we obtain the same query time while reducing the space usage by a factor . We also significantly improve upon the previously best known linear space index by Lam et al. , as we match the linear space usage while improving the worst-case query time from to provided . Our solution is faster than the simple suffix tree index for . Thus, for sufficiently small we improve upon the previously known unbounded wildcard indexes.
The main idea of the solution is to combine an ART decomposition  of the suffix tree for with the LCP data structure. The suffix tree is decomposed into a number of logarithmic-sized bottom trees and a single top tree. We introduce a new variant of the LCP data structure for use on the bottom trees, which supports queries in logarithmic time and linear space. The logarithmic size of the bottom trees leads to LCP queries in time . On the top tree we use the LCP data structure by Cole et al.  to answer queries in time . The number of LCP queries performed during a search for is , yielding the term in the query time. The reduced size of the top tree causes the index to be linear in size.
Let be a string of length from an alphabet of size . For , there is a -bounded wildcard index using space. The index can report the occurrences in of a pattern with characters and wildcards in time .
The theorem provides a time-space trade-off for -bounded wildcard indexes. Compared to the index by Cole et al. , we reduce the space usage by a factor by increasing the branching factor from to . For the index is identical to the index by Cole et al. The result is obtained by generalizing the wildcard index described by Cole et al. We use a heavy -tree decomposition, which is a new technique generalizing the classic heavy path decomposition by Harel and Tarjan . This decomposition could be of independent interest. We also show that for the same technique yields an index with query time using space , where is the height of the suffix tree for .
Let be a string of length from an alphabet of size . There is a -bounded wildcard index for using space. The index can report the occurrences of a pattern with characters and wildcards in time .
To our knowledge this is the first linear time index with a non-trivial space bound. The result improves upon the space usage of the simple linear time index when . To achieve this result, we use the space index to obtain a black-box reduction that can produce a linear time index from an existing index. The idea is to build the space index with support for short patterns, and query another index if the pattern is long. This technique is closely related to the concept of filtering search introduced by Chazelle  and has previously been applied for indexing problems [3, 6]. The theorem follows from applying the black-box reduction to the index of Theorem 1.
1.2.1 Variable Length Gaps
We also show that the three indexes support searching for query patterns with variable length gaps, i.e., patterns of the form , where denotes a variable length gap that matches an arbitrary substring of length between and , both inclusive.
String indexing for patterns with variable length gaps has applications in information retrieval, data mining and computational biology [16, 18, 31, 29, 33]. In particular, the PROSITE data base [21, 5] uses patterns with variable length gaps to identify and classify protein sequences. The problem is a generalization of string indexing for patterns with wildcards, since a wildcard is equivalent to the variable length gap . Variable length gaps are also known as bounded wildcards, as a variable length gap can be regarded as a bounded sequence of wildcards.
String indexing for patterns with variable length gaps is equivalent to string indexing for patterns with wildcards, with the addition of allowing optional wildcards in the pattern. An optional wildcard matches any character from or the empty string, i.e., an optional wildcard is equivalent to the variable length gap . Conversely, we may also consider a variable length gap as consecutive wildcards followed by consecutive optional wildcards.
Lam et al.  introduced optional wildcards in the pattern and presented a variant of their solution for the string indexing for patterns with wildcards problem. The idea is to determine potential matches and verify complete matches using interval stabbing on the possible positions for the subpatterns. This leads to an unbounded optional wildcard index with query time and space usage . Here and denotes the number of matches of in , and since in the worst case, the worst case query time is . Recently, Lewenstein  considered the special case where the pattern contains at most gaps and for all , i.e., the gaps are non-variable and of length at most . Using techniques similar to those by Cole et al. , he gave a bounded index with query time using space , where is the number of gaps in the pattern.
The related string matching with variable length gaps problem, where the text may not be preprocessed in advance, has recieved some research attention recently [4, 34, 30, 33, 17]. However, none of the results and techniques developed for this problem appear to lead to non-trivial bounds for the indexing problem.
Our Results for Variable Length Gaps
To introduce our results we let and denote the sum of the lower and upper bounds on the variable length gaps in , respectively. Hence and denote the number of normal and optional wildcards in , respectively. A wildcard index with support for optional wildcards is called an optional wildcard index. As for wildcard indexes, we distinguish between bounded and unbounded optional wildcard indexes. A -bounded optional wildcard index supports patterns containing normal wildcards and optional wildcards. An unbounded optional wildcard index supports patterns with no restriction on the number of normal and optional wildcards.
To accommodate for variable length gaps in the pattern, we only need to modify the way in which the wildcard indexes are searched, leading to the following new theorems. The proofs are given in Section 7.
Let be a string of length from an alphabet of size . There is an unbounded optional wildcard index for using space. The index can report the occurrences of a pattern with characters, wildcards and optional wildcards in time , where and .
Let be a string of length from an alphabet of size . For , there is a -bounded optional wildcard index for using space. The index can report the occurrences of a pattern with characters, wildcards and optional wildcards in time , where and .
Let be a string of length from an alphabet of size . There is a -bounded optional wildcard index for using space. The index can report the occurrences of a pattern with characters, wildcards and optional wildcards in time , where and .
These results completely generalize our previous solutions, since if the query pattern only contains variable length gaps of the form , the problem reduces to string indexing for patterns with wildcards. In that case and we obtain exactly Theorem 1, Theorem 2 and Theorem 3.
We introduce the following notation. Let be a pattern consisting of strings (subpatterns) interleaved by wildcards. The substring starting at position in is an occurrence of if and only if each subpattern matches the corresponding substring in . That is,
where denotes the substring of between indices and , both inclusive. We define for , for and for . Furthermore is the number of characters in , and we assume without loss of generality that and .
Let and denote the prefix and suffix of of length and , respectively. Omitting the subscripts, we let and denote the set of all non-empty prefixes and suffixes of , respectively. We extend the definitions of prefix and suffix to sets of strings as follows.
A set of strings is prefix-free if no string in is a prefix of another string in . Any string set can be made prefix-free by appending the same unique character to each string in .
2.1 Trees and Tries
For a tree , the root is denoted , while is the number of edges on a longest path from to a leaf of . A compressed trie is a tree storing a prefix-free set of strings . The edges are labeled with substrings of the strings in , such that a path from the root to a leaf corresponds to a unique string in . All internal vertices (except the root) have at least two children, and all labels on the outgoing edges of a vertex have different initial characters.
A location may refer to either a vertex or a position on an edge in . Formally, where is a vertex in and is a prefix of the label on an outgoing edge of . If , we also refer to as an explicit vertex, otherwise is called an implicit vertex. There is a one-to-one mapping between locations in and unique prefixes in . The prefix corresponding to a location is obtained by concatenating the edge labels on the path from to . Consequently, we use and interchangeably, and we let denote the length of . Since is assumed prefix-free, each leaf of is a string in , and conversely. The suffix tree for denotes the compressed trie over all suffixes of , i.e., . We define as the subtrie of rooted at . That is, contains the suffixes of strings in starting from . Formally, , where
2.2 Heavy Path Decomposition
For a vertex in a rooted tree , we define to be the number of leaves in , where denotes the subtree rooted at . We define . The heavy path decomposition of , introduced by Harel and Tarjan , classifies each edge as either light or heavy. For each vertex , we classify the edge going from to its child of maximum weight (breaking ties arbitrarily) as heavy. The remaining edges are light. This construction has the property that on a path from the root to any vertex, heavy paths are traversed. For a heavy path decomposition of a compressed trie , we assume that the heavy paths are extended such that the label on each light edge contains exactly one character.
3 The LCP Data Structure
Cole et al.  introduced the the Longest Common Prefix (LCP) data structure, which provides a way to traverse a compressed trie without tracing the query string one character at a time. In this section we give a brief, self-contained description of the data structure and show a new property that is essential for obtaining Theorem 1.
The LCP data structure stores a collection of compressed tries over the string sets . Each is a set of substrings of the indexed string . The purpose of the LCP data structure is to support LCP queries
: Returns the location in where the search for the string stops when starting in location .
If is the root of , we refer to the above LCP query as a rooted LCP query. Otherwise the query is called an unrooted LCP query. In addition to the compressed tries , the LCP data structure also stores the suffix tree for , denoted where . The following lemma is implicit in the paper by Cole et al. .
Lemma 1 (Cole et al. ).
Provided has been preprocessed in time , the LCP data structure can answer rooted LCP queries on for any suffix of in time using space . Unrooted LCP queries on can be performed in time using additional space.
We extend the LCP data structure by showing that support for slower unrooted LCP queries on a compressed trie can be added using linear additional space.
Unrooted LCP queries on can be performed in time using additional space.
We initially create a heavy path decomposition for all compressed tries . The search path for starting in traverses a number of heavy paths in . Intuitively, an unrooted LCP query can be answered by following the heavy paths that the search path passes through. For each heavy path, the next heavy path can be identified in constant time. On the final heavy path, a predecessor query is needed to determine the exact location where the search path stops.
For a heavy path , we let denote the distance that the search path for follows . Cole et al.  showed that can be determined in constant time by performing nearest common ancestor queries on . To answer we identify the heavy path of that is part of and compute the distance as described by Cole et al. If leaves on a light edge, indexing distance into from yields an explicit vertex . At , a constant time lookup for determines the light edge on which leaves . Since the light edge has a label of length one, the next location on that edge is the root of the next heavy path. We continue the search for the remaining suffix of from recursively by a new unrooted LCP query . If is the heavy path on which the search for stops, the location at distance (i.e., the answer to the original LCP query) is not necessarily an explicit vertex, and may not be found by indexing into . In that case a predecessor query for is performed on to determine the preceding explicit vertex and thereby the location . Answering an unrooted LCP query entails at most recursive steps, each taking constant time. The final recursive step may require a predecessor query taking time . Consequently, an unrooted LCP query can be answered in time using additional space to store the predecessor data structures for each heavy path. ∎
4 An Unbounded Wildcard Index Using Linear Space
In this section we show how to obtain Theorem 1 by applying an ART decomposition on the suffix tree for and storing the top and bottom trees in the LCP data structure.
4.1 ART Decomposition
The ART decomposition introduced by Alstrup et al.  decomposes a tree into a single top tree and a number of bottom trees. The construction is defined by two rules:
A bottom tree is a subtree rooted in a vertex of minimal depth such that the subtree contains no more than leaves.
Vertices that are not in any bottom tree make up the top tree.
The decomposition has the following key property.
Lemma 3 (Alstrup et al. ).
The ART decomposition with parameter for a rooted tree with leaves produces a top tree with at most leaves.
4.2 Obtaining the Index
Applying an ART decomposition on with , we obtain a top tree and a number of bottom trees each of size at most . From Lemma 3, has at most leaves and hence vertices since is a compressed trie.
To facilitate the search, the top and bottom trees are stored in an LCP data structure, noting that these compressed tries only contain substrings of . Using Lemma 2, we add support for unrooted time LCP queries on the bottom trees using additional space in total. For the top tree we apply Lemma 1 to add support for unrooted LCP queries in time using additional space. Since the branching factor is not reduced, LCP queries, each taking time , are performed for the subpattern . This concludes the proof of Theorem 1.
5 A Time-Space Trade-Off for -Bounded Wildcard Indexes
In this section we will show Theorem 2. We first introduce the necessary constructions.
5.1 Heavy -Tree Decomposition
The heavy -tree decomposition is a generalization of the well-known heavy path decomposition introduced by Harel and Tarjan . The purpose is to decompose a rooted tree into a number of heavy trees joined by light edges, such that a path to the root of traverses at most a logarithmic number of heavy trees. For use in the construction, we define a proper weight function on the vertices of , to be a function satisfying Observe that using the number of vertices or the number of leaves in the subtree rooted at as the weight of satisfies this property. The decomposition is then constructed by classifying edges in as being heavy or light according to the following rule. For every vertex , the edges to the heaviest children of (breaking ties arbitrarily) are heavy, and the remaining edges are light. For this results in a heavy path decomposition. Given a heavy -tree decomposition of , we define to be the number of light edges on a path from the vertex to the root of . The key property of this construction is captured by the following lemma.
For any vertex in a rooted tree and
Consider a light edge from a vertex to its child . We prove that , implying that . To obtain a contradiction, suppose that . In addition to , must have heavy children, each of which has a weight greater than or equal to . Hence
which is a contradiction. ∎
Lemma 4 holds for any heavy -tree decomposition obtained using a proper weight function on . In the remaining part of the paper we will assume that the weight of a vertex is the number of leaves in the subtree rooted at . See Figure 1 for two different examples of heavy -tree decompositions.
We define to be the maximum light depth of a vertex in , and remark that for , . For a vertex in a compressed trie , we let denote the set of strings starting in one of the light edges leaving . That is, is the union of the set of strings in the subtries where is the first location on a light outgoing edge of , i.e., .
5.2 Wildcard Trees
We introduce the -wildcard tree, denoted , where is a chosen parameter. This data structure stores a collection of strings in a compressed trie such that the search for a pattern with at most wildcards branches to at most locations in when consuming a single wildcard of . In particular for , the search for never branches and the search time becomes linear in the length of . For a vertex , we define the wildcard height of to be the number of wildcards on the path from to the root. Intuitively, given a wildcard tree that supports wildcards, support for an extra wildcard is added by joining a new tree to each vertex with wildcard height by an edge labeled . This tree is searched if a wildcard is consumed in . Formally, is built recursively as follows.
Construction of : Produce a heavy -tree decomposition of , then for each internal vertex join to the root of by an edge labeled . Let .
The construction is illustrated in Figure 2. Since a leaf in a compressed trie is obtained as the suffix of a string , we assume that inherits the label of in case the strings in are labeled. For example, when denotes the suffixes of , we will label each suffix in with its start position in . This immediately provides us with a -bounded wildcard index. Figure 3 shows some concrete examples of the construction of when is a set of labeled suffixes.