BWT Columns and LF-Mapping
Append a unique sentinel $ that is lexicographically smaller than every text symbol. Sorting the suffixes of banana$ gives suffix-array order $[6,5,3,1,0,4,2]$. The first column $F$ contains the first symbols of those sorted suffixes, while the Burrows-Wheeler column $L$ contains the symbol immediately preceding each suffix in the original cyclic text; here, $L=\texttt{annb\$aa}$.
Stable sorting preserves the relative order of equal symbols, so the $k$-th occurrence of a symbol in $L$ corresponds to the $k$-th occurrence of that symbol in $F$. Let $C[c]$ be the number of text symbols strictly smaller than $c$, and let $\operatorname{rank}_c(i)$ count occurrences of $c$ in the half-open prefix $L[0\dots i)$. Then
$$ \operatorname{LF}(i)=C[L[i]]+\operatorname{rank}_{L[i]}(i). $$
Moving from row $i$ to $\operatorname{LF}(i)$ prepends $L[i]$ to that row's suffix. Repeating LF steps therefore walks backward through the text and is also the basis of BWT inversion.
| Row $i$ | $\text{SA}[i]$ | Sorted suffix | $F$ occurrence | $L$ occurrence | $\operatorname{LF}(i)$ |
|---|---|---|---|---|---|
| 0 | 6 | $ | $ #1 | a #1 | 1 |
| 1 | 5 | a$ | a #1 | n #1 | 5 |
| 2 | 3 | ana$ | a #2 | n #2 | 6 |
| 3 | 1 | anana$ | a #3 | b #1 | 4 |
| 4 | 0 | banana$ | b #1 | $ #1 | 0 |
| 5 | 4 | na$ | n #1 | a #2 | 2 |
| 6 | 2 | nana$ | n #2 | a #3 | 3 |
Backward Search
All suffix-array rows initially match the empty pattern, so backward search starts with the half-open interval $[l,r)=[0,n)$. To prepend character $c$ to the portion of the pattern already processed, restrict the interval to rows whose preceding BWT character is $c$:
$$ l'=C[c]+\operatorname{rank}_c(l),\qquad r'=C[c]+\operatorname{rank}_c(r). $$
The new interval is empty when $l'\ge r'$. Otherwise, after processing the pattern from right to left, every suffix-array row in $[l,r)$ begins with the complete pattern. Its occurrence count is therefore $r-l$; locating occurrences additionally requires recovering the suffix-array values for those rows.
Backward Search
Search for ana in banana$, whose BWT is annb$aa. The cumulative counts are $C[\$]=0$, $C[a]=1$, $C[b]=4$, and $C[n]=5$.
- Begin with every row: $[0,7)$.
- Read the pattern's final
a: $[0,7)\rightarrow[1,4)$. - Prepend
n: $[1,4)\rightarrow[5,7)$. - Prepend the first
a: $[5,7)\rightarrow[2,4)$.
Rows 2 and 3 are the suffixes ana$ and anana$. Their suffix-array values are 3 and 1, so the reported text positions are $[1,3]$ after sorting. Notice that backward search performs one pair of rank queries per pattern symbol and never compares the pattern against individual suffixes.
Rank, Select, and Wavelet Structures
A dense occurrence table stores $\operatorname{rank}_c(i)$ for every character and position, giving constant-time rank but using $O(n|\Sigma|)$ space. Checkpointed counts reduce space and scan a short block per query. For small alphabets, one bitvector per symbol plus word-level popcount is often both compact and fast.
A wavelet tree recursively partitions the alphabet and stores one routing bitvector at each node. A rank query follows the queried symbol's root-to-leaf path, translating the prefix length with rank at each level, for $O(\log |\Sigma|)$ time and approximately $n\log |\Sigma|$ bits before succinct-bitvector overhead. Wavelet matrices improve layout; Huffman-shaped trees favor frequent symbols; and run-length-compressed BWT structures exploit highly repetitive texts.
Building a Practical FM-Index
Construction needs the suffix array, BWT, cumulative counts, and a rank structure. Counting stops after backward search and can remain highly compressed. Locating must recover suffix-array positions: sample selected SA values, repeatedly apply LF from an unsampled row until reaching a sample, and return $(\text{sample}+\text{steps})\bmod n$. Sampling every $d$ text positions uses roughly $n/d$ samples but can require up to $d-1$ LF steps per located occurrence.
The compact implementation below uses integer encoding with sentinel 0, a dense prefix-count table for clarity, and samples suffix positions divisible by the chosen rate. Production indexes replace the naive suffix sort and dense rank table with scalable suffix-array construction and succinct rank/select structures, while preserving the same search equations.