Chapter 8: Strings and Tries
Exercise 8.1
As mentioned in the book, [zN]BP(z) satisfies the same recurrence as the generalized Fibonacci numbers of Exercise 4.18. This gives
The other one can be derived from the formula of Theorem 8.2 by rearranging terms
Now, we extract the coefficient of zN on both sides. For N>P the RHS is 0, thus
Remember that bN represents the number of bitstrings of length N with no runs of P consecutive 0s. If the length of a bitstring is strictly less than P, it’s physically impossible for the string to contain a run of P consecutive 0s. Therefore, every possible bitstring of length N is valid. The initial values are
Observe that bP can be calculated from the first recurrence, so the second one becomes fully defined, too.
Exercise 8.2
We need to solve the following inequality
with P=3. Using a computer algebra system and Table 8.3, we get N=57.
We can also compute the values of b56 and b57 using one of the recurrences from the previous exercise (for example, with the help of a Python script). The exact calculation would confirm the validity of our stated result.
Exercise 8.3
This time we should rely on the asymptotic formula from the previous exercise (with the RHS of 0.5), as exact calculations based on recurrence would become prohibitively slow. The parameters are (these can be attained using a computer algebra system):
β32≈1.9999999997671694
c32≈1.0000000034924597
We get N=5,954,088,975.
Exercise 8.4 🌟
This exercise showcases a general approach of extracting coefficients from GFs; transforming the GF into a geometric series and expanding the corresponding binomial.
We start with the formula from Theorem 8.2 and extract the coefficient [zN]BP(z). We can do this by expanding the rational function into a power series using the geometric series formula and the binomial theorem. Let’s rearrange terms
This gives
We can transform S(z) into a geometric series and expand
We want to find the coefficient of a general term zK. This gives
We have
Now we substitute K=N and K=N−P into our general coefficient formula to solve our original equation
Putting everything together, we get
Exercise 8.5
Let’s build our BGF F(z,u) using the symbolic method; F designates a class of bitstrings with leading 1s (which may be zero). The beauty of this approach is that the construction itself fully describes the underlying combinatorial decomposition, so everything is explicit. We have
Notice that we must mark the leading 1s. This translates to
Now, we simply follow the recipe laid out in Table 3.5 in the book to find the moments (mechanical steps are omitted):
μN=1−2−N,
σN=2−(2N+1)2−N−2−2N.
Exercise 8.6
We just apply Theorem 8.2 and its corollary to get
Exercise 8.7 🌟
This exercise demonstrates how to track extremal parameters, where we cannot easily construct a BGF using a single, simple symbolic decomposition.
Let L(z,u) be our BGF, where z marks the length of the string and u marks the length of the longest run of 0s. Instead of trying to directly find the generating function for bitstrings where the longest run is exactly k, it’s much easier to think about boundaries. A bitstring has a longest run of 0s that is ≤k if and only if it has no runs of k+1 consecutive zeros. Therefore, the OGF for strings whose longest run of 0s is ≤k is exactly Bk+1(z). This gives
We can now sum over all k and multiply by uk to get
Exercise 8.8 🌟
This exercise introduces a different methodology for computing variance, when length itself is a random variable.
Let X be the random variable denoting the position of the end of the first run of P 0s ("wait time"); the length of the bitstring (prefix) is the random variable itself. The book establishes the relationship
To compute the variance we need a different paradigm than the BGF method and Table 3.5. Because the thing we are measuring is already the length of the bitstring, we don't need a second variable u. We can extract everything we need directly from z. Let's define a new generating function S(z) for these tail probabilities
The second corollary of Theorem 8.2 says that
To find the variance, besides the mean, we need the second factorial moment E[X(X−1)]. We’ve
Equate this quantity with the one derived in a different manner. Let’s differentiate both sides of our basic equation and evaluate it at z=1. This gives
Substitute everything into the standard variance identity (see Section 1.7 in the book)
Plugging everything into the above formula and simplifying, we get
Therefore,
Exercise 8.9
The next Python script generates the plot by evaluating the GF from the book.

Exercise 8.10 🌟
This exercise encompasses several important details from probability theory and showcases how even simple programs demand a careful analysis.
The second corollary of Theorem 8.2 specifies the expected length of the bitstring (i.e., the expected position where the pattern finally ends). Because of the way the nested for loops work in Program 8.1, the algorithm re-examines characters whenever a partial match fails. This means the number of bits examined will be strictly greater than the expected length of the string.
We can view our random bitstring as a sequence of "failed blocks" followed by one "success block." A failed block can be described as 0k1, where 0≤k<P. Processing such a failed block demands (k+1)(k+2)/2 bit comparisons (a total of subsequent partial and failed matches). Processing the final P 0s requires additional P bit comparisons.
The probability of seeing P consecutive 0s from an arbitrary position is p=2−P. We can model the run of our program as a series of Bernoulli trials with probability p of success. The random variable denoting the number of failures before the first success is governed by the geometric distribution. Its expected value is (1−p)/p=2P−1.
Now, what is the probability that a failed block has exactly k zeros? The unconditional probability of reading exactly k 0s and then 1 is simply (1/2)k+1. The total probability of any failure occurring (meaning we hit 1 before P 0s) is the sum of these probabilities for all valid k. Thus,
The conditional probability of seeing the pattern 0k1, given that the block is a failure
The expected number of comparisons per failed block
Now, we can express the total expected number of comparisons on failed blocks, relying on Wald's equation
The exact expected number of bits examined to find the first run of P 0s is
Exercise 8.11
This is a simple application of the corollary of Theorem 8.3:
c(z)=1⟹μ=2P,
c(z)=1⟹μ=2P,
c(z)=∑i=0P/2−1z2i⟹μ=34(2P−1),
c(z)=∑i=0(P−1)/2z2i⟹μ=34(2P−21).
Exercise 8.12
To find the pattern that appears earliest, we need to minimize c(1/2). Since c(z) always starts with a 1, the absolute smallest it can possibly be is just c(z)=1. This happens when a pattern has zero internal overlaps—meaning no prefix matches any suffix. We’ve seen such patterns in the previous exercise.
Patterns consisted of all 0s or 1s are likely to appear the latest. They’re maximally autocorrelated, so c(1/2) is maximized.
Exercise 8.13
The standard deviation does depend on the pattern, because the expected value is dictated by the pattern's autocorrelation polynomial c(z), the spread of that distribution (the variance and standard deviation) is fundamentally locked to that same polynomial. Following the steps from Exercise 8.8, we can see that the standard deviation depends both on c(1/2) and c′(1/2).
Two different patterns of the same length P can technically have the same expected waiting time if their c(1/2) values somehow match (see Figure 8.5 in the book), but their variances will almost certainly differ because c′(1/2) factors in where those overlaps occur.
Exercise 8.14 🌟
This exercise generalizes Theorem 8.3 and its second corollary to larger alphabets.
The GF for the number of strings over an M-character alphabet that do not contain the specific pattern of length P is
The expected position of the end of the first occurrence of a M-character pattern with autocorrelation polynomial c(z) is given by μ=MPc(1/M).
In our case, M=32, P=44 and c(z)=1. Therefore, the expected number of characters typed before the monkey hits upon the given phrase is 3244.
Exercise 8.15
This is a variation of the previous exercise. The pattern has some overlaps, so
Since P=18 and M=32, the expected number of characters typed before the monkey hits upon the given phrase is 3218+325.
Exercise 8.16
Table 8.6 already provides an answer for height no greater than 4. The following list gives the remaining OGFs and REs:
height≤5,
(1(1(1(1(10)*0)*0)*0)*0)*, A(z)=1−5z+6z2−z31−4z+3z2,height≤6,
(1(1(1(1(1(10)*0)*0)*0)*0)*0)*, A(z)=1−6z+10z2−4z31−5z+6z2−z3.
Exercise 8.17 🌟
This exercise introduces an advanced mechanism for constructing restrictive REs.
Theorem 8.3 says that the GF is
Finding a regular expression for strings avoiding a pattern is notoriously complex because REs are generative (they build strings) rather than restrictive. Unfortunately, there is no reverse mapping from an OGF back to unambiguous RE. We’ll model the pattern-matching process as a DFA and leverage Arden's Theorem.
101101. Because we are looking to avoid the pattern, any string that reaches state 6 (Trap) stays there and is essentially rejected. All other states are accepting.The system of equations for the given DFA is
Solving it and simplifying leads to
Here is the translation of this RE into a form recognized by tools. The + operator is a syntactic sugar for one or many, while the original + is replaced with the | symbol. Notice that the expression is anchored to capture the entire content.
One handy on-line tool for working with REs is Regular Expressions 101.
Exercise 8.18
The word disjoint means that the second search starts after the first match is "consumed." Since the bitstring is generated randomly, the sequence of bits generated after the first match is statistically identical to a brand new, entirely independent random bitstring. Therefore, due to this independence (memoryless property), the average position of the end of the second disjoint string of P 0s is exactly double the expected position of the first occurrence
Exercise 8.19
The RE 0*00 is unambiguous, so there is only one way to derive a string of N 0s.
The RE 0*00* allows N possibilities. Observe that the number of 0s taken on the first position completely determines the number of 0s on the last one, and vice versa. The zero in the middle is forced, so we can choose between 0 and N−1 for the first occurrence of 0*.
Exercise 8.20
The last plot and sequence is wrong. By taking a close inspection of the peak, we can see that it’s height is 5 instead of 4. This becomes apparent by looking at the generalized RE below.
(10)3 (1(10)1(10)20(10)0) (10)3 (1(10)1(10)30(10)20) (10)2
(10)3 (1(10)1(10)20(10)0) (10)3 (1(10)1404(10)20) (10)2
Exercise 8.21
Table 8.5 lists all 4-bit patterns together with their waiting times. The expected number of 0s appearing before the first occurrence of each of the bit patterns of length 4 in a random bitstring is exactly half of their waiting times, minus the number of 0s in the patterns themselves. This gives
Group 1: Wait Time = 30
0000: 15−4=111111: 15−0=15
Group 2: Wait Time = 16
0001: 8−3=51000: 8−3=50011: 8−2=61100: 8−2=60111: 8−1=71110: 8−1=7
Group 3: Wait Time = 18
0010: 9−3=60100: 9−3=60110: 9−2=71001: 9−2=71011: 9−1=81101: 9−1=8
Group 4: Wait Time = 20
0101: 10−2=81010: 10−2=8
Exercise 8.22
Seemingly the answer follows directly from the second corollary of Theorem 8.2. But the situation is a bit more complicated, since we must allow any sort of 2k alternating 0s and 1s to appear. For example, a monkey may start with one type (010101...) and switch to another (101010...) in the middle. These events could happen in any order. Because these two strings share so many prefixes, waiting for "whichever comes first" is significantly faster.
We can solve this by translating the bits into a sequence of transitions. Let’s create a new sequence where 1 means a different key was typed than before and zero if the same key was hit. Therefore, waiting for any alternating string of 2k bits is mathematically identical to waiting for a solid block of 2k−1 1s in a random bitstring. Now, we can apply the second corollary of Theorem 8.2. Since a sequence of N transitions requires N+1 actual keystrokes (a starting character must be there before we can transition away from it), we need to add 1 to the total. The answer is 4k−1.
Exercise 8.23
The book explicitly lists state 0 at front as a starting point. The state transitions are (including that extra 0 at the beginning)
The following Python script prints out these transitions for the given pattern in the book.
Exercise 8.24
This is a variation of the previous exercise. The state transitions are
Exercise 8.25
Once we reach state 2 in 2 steps, we need additional 3 steps to reach it again. Therefore, the text string of length 25 that maximizes (among all strings of length 25) the number of times the KMP automaton from Figure 8.3 reaches step 2 is
Notice that the solution isn’t unique.
Exercise 8.26

Exercise 8.27
The program from Exercise 8.23 (the DFA table and the function call arguments should be changed) generates the following output:
Exercise 8.28
For the pattern 101010... the state transition table is (listed horizontally as ordered pairs): (0, 1), (2, 1), (0, 3), (4, 1), ... , (0, 2k-1), (2k, 1).
For the pattern 010101... the state transition table is (listed horizontally as ordered pairs): (1, 0), (1, 2), (3, 0), (1, 4), ..., (2k-1, 0), (1, 2k).
Exercise 8.29
Notice that any occurrence of 0 or 2 would reset the automaton (bring it back to state 0). The pattern resembles the one from the previous exercise, so the transition table is similarly constructed (drawn vertically):
The program from Exercise 8.23 should be altered to reflect the new DFA table and text string. It produces the following output:
Exercise 8.30 🌟
This exercise provides a direct proof that the language recognized by a deterministic FSA has an OGF that is rational.
Let's assume we’ve a DFA with K states, numbered 0 to K−1. Let state 0 be the start state and Fi(z) be the OGF that enumerates all valid paths starting from state i and ending in an accept state. The language recognized by the entire DFA is simply F0(z).
A valid string starting from state i is formed by taking exactly one transition (which costs one character, or z) to a neighboring state j (could also be a self-transition), and then appending any valid string from state j. We can write this as a linear equation for every state
where:
Mi,j is the number of transitions from state i to state j (usually 0 or 1, but could be more if multiple characters lead to the same state).
ci=1 if state i is an accept state (since the empty string ϵ is a valid way to stop right there), and ci=0 otherwise. Exercise 8.17 shows how to apply this in practice.
As usual, a system of such linear equations is most easily handled in matrix form. Let F(z) be the column vector of our OGFs, c be the column vector of our accept states, and M be the adjacency matrix of the DFA. This gives
The solution is
Cramer's rule for matrix inversion says that
The matrix (I−zM) contains only linear polynomials. The determinant of a matrix of polynomials is calculated purely by adding and multiplying those polynomials. Therefore, the denominator is guaranteed to be a polynomial in z. Finally, the adjugate matrix is formed by calculating the determinants of sub-matrices. Thus, every entry in the numerator is also guaranteed to be a polynomial in z.
Because F0(z) (the OGF of our language) is calculated strictly by dividing a polynomial numerator by a polynomial denominator, it is by definition a rational function. This concludes the proof.
Exercise 8.31
The following Python script implements the solver for the model presented in the previous exercise. To showcase that it works properly, it’s setup to reproduce the OGF from Exercise 8.17.
It outputs exactly the same OGF as formulated in Theorem 8.3, although not in the most simplified form.
Exercise 8.32
The number of bitstrings of length 2N with equal number of 0s and 1s is given in the book
We just need to count all possible even length prefixes (including an empty prefix) with equal number of 0s and 1s and divide the result by the total number of bitstrings of length N. This gives
Exercise 8.34 demonstrates another path to a solution via linearity of expectation. In the context of 1D random walks (which this problem directly maps to, where 0 is a step left and 1 is a step right), the derived formula calculates the expected number of returns to the origin!
Exercise 8.33
This directly follows from Exercise 6.10, where N denotes the length of a random bitstring
Exercise 8.34
The first question can be answered by employing linearity of expectation. Let Xi be an indicator random variable that a prefix of length 2i+k has k more 0s than 1s. Therefore,
The answer to the second question follows from generalizing an approach from Exercise 6.10, where k was fixed at 1. So, by renaming variables, adjusting the criteria for the lower bound, and setting the touching line to y=−k, we get
Exercise 8.35 🌟
This exercise expands on the topic about CFGs to illuminate some limitations and shows how to tackle more complicated constraints by using a deterministic FSA.
A stack with a fixed capacity of M is mathematically identical to a deterministic FSA with exactly M+1 states. A push is a transition from state i to state i+1. A pop is a transition from state i to state i−1. If we’re in state 0 and read a pop, it's a trap (reject). If we’re in state M and read a push, it's a trap (reject). Push and pop operations may be encoded as 1 and 0, or vice versa. Therefore, the problem transforms into tracking random bitstrings of length N, where the number of 0s and 1s remain bounded.
We cannot use a ballot like CFG, since it’s impossible to specify an upper limit. It could only model a stack of infinite capacity. Let’s see what do we get if we plug an adjacency matrix for M=3 into the program from Exercise 8.31. The new input is
It outputs the OGF for Fibonacci numbers
Let’s try M=4 with the following setup
The program gives
It turns out that the growth rate for the total number of legal sequences is dictated by the maximum eigenvalue of the adjacency matrix. At any rate, to get the desired probability we must divide this amount by 2N.
Exercise 8.36 🌟
This exercise presents a case study in using CFGs for enumeration problems.
Let our operations be:
I: Insert (1 choice)
R: Remove (2 choices: R1 or R2)
We can reuse the idea from Figure 8.2 in the book by placing any valid sequence into an envelope of one insert and remove operations. This gives our CFG
The initial condition is S(0)=1, so
If we use a substitution u=2z2, then it immediately transforms into the familiar OGF for Catalan numbers. Thus,
The number of legal sequences of length N=2n is exactly 2nTn. The number of all possible sequences is 3N=9n (recall that we have 3 operations). Therefore,
Exercise 8.37
We build upon the previous exercise. A new nonterminal is <T> which designates a potentially traced (inspected) sequence. The Inspect (Watch) operation is denoted by W. This gives
The grammar results in the following system of equations:
S(z)=1+z2T(z)S(z)
T(z)=1+zT(z)+z2T(z)2
We can immediately solve the second equation
It’s a left shifted OGF for Motzkin numbers (see Chapter 6 of the book about t-restricted trees). Thus,
The probability that a random sequence of operations of length N is legal is
The asymptotic estimation is the result of applying the universal transfer theorem of analytic combinatorics, as hinted in the book.
Exercise 8.38
Observe that the number of legal balanced sequences matches the enumeration of general Catalan trees. This bijection follows from the parenthesis system representation of those trees. Therefore, we can find the total probability of success by evaluating the G(z) at z=1/2. This gives
This is remarkable, that a monkey has only a 50% chance of succeeding. Nonetheless, the expected number of keystrokes is even more dizzying. We have
The average number of characters typed before the monkey hits upon a legal balanced sequence is
More precisely, the average length of a balanced legal sequence tends to infinity, thus the number of keystrokes tends to infinity, as well.
Exercise 8.39 🌟
This exercise leverages the concept of martingales for computing the expected number of keystrokes.
The experiment terminates the exact moment the monkey types a palindrome of 10 or 11 characters long. We’ll spawn two gamblers at every step t:
Gambler A brings $1 and bets that the 10 characters from t to t+9 will form a length 10 palindrome.
Gambler B brings $1 and bets that the 11 characters from t to t+10 will form a length 11 palindrome.
To form a length 10 palindrome, 5 independent pairs of characters must match. Gambler A waits for the first 5 characters to be typed, and then bets their fortune on the 6th character matching the 5th, the 7th matching the 4th, etc. Every time they are right, their money multiplies by 26. Gambler B also requires exactly 5 pairs to match (the center character is free). At any rate, the maximum winning amount is 265.
Since we "work" with 2 gamblers at a time, we have
where N is a random variable denoting the stopping time. When the game stops at time N, it stops because either a length 10 or length 11 palindrome was just reached.
If it stops on a length 10 palindrome, then Gambler A who arrived 10 steps ago wins the grand prize of 265. If it stops on a length 11 palindrome, then Gambler B who arrived 11 steps ago wins the grand prize of 265. Gamblers who arrived very recently, and haven't finished their 10 or 11 characters yet, are "unresolved" with expected earning of $1. There are 9+10=19 such gamblers from both groups. The total payout is $(265+19).
The cited post about martingales uses a concrete palindrome ABRACADABRA. It clearly has nontrivial autocorrelation, so a gambler who came 4 steps before the end earns $264. Again, in that setup gamblers must guess the exact palindrome, hence they always start with A. In our case, we don’t have this. Knowing that a string is a palindrome only tells us that the left side mirrors the right side. It tells nothing about whether the letters inside the left side match each other. Because the "free" letters of the palindrome are perfectly random, any overlapping gambler who gets far enough to start placing bets is effectively just betting that two randomly chosen letters will match. Since all bets are fair, their expected wealth is exactly the they walked in with! Of course, those who had no chance to bet will keep their money, so we also include them in the payout.
Because the total expected payout is unconditionally the same regardless of whether the monkey triggers the length 10 or length 11 condition, we can solve directly for the expected number of keystrokes
Another approach is to use the method described in the paper String overlaps, pattern matching, and nontransitive games and build autocorrelation polynomials for palindromes of length 10 and 11. Interestingly, the coefficients of these polynomials are exactly the payouts for gamblers from each group. The constant term is 265, whilst all other coefficients are 1. They only differ in length. The martingales method essentially explains the rationale behind those OGFs mentioned in the book and this cited paper.
The methodology laid out above easily generalizes to any k≥10.
Exercise 8.40 🌟
This exercise showcases additional techniques pertaining to the application of CFGs in analysis.
Since spaces can appear anywhere in a regular expression they aren’t essential for evaluating the validity of a sequence. The monkey is typing "essential" keys with probability 31/32, and "noise" (spaces) with probability 1/32. By Wald's Lemma (see Exercise 8.21), we can completely remove the space key from the board. We simply calculate everything in terms of a 31-key keyboard and then multiply the result by 32/31, the expected number of total keystrokes required to produce one essential keystroke, to account for the spaces typed in between.
Let's build the unambiguous CFG for a standard regular subexpression E over the 30 essential keys (excluding space and period). Every binary operation brings its own parentheses to guarantee unambiguous parsing, while the unary star operator does not need them. We’ve
It’s possible to craft a bit different grammar, but the methodology remains the same.
The final language becomes <L>:=(<E>). (notice the period at the end). Theorem 8.6 gives
Because the regular expression is enclosed in strict parentheses and ends with a unique period that never appears anywhere else, the language is prefix-free, suffix-free, and internally non-overlapping. Therefore, we can regard every keystroke as a Bernoulli trial, so the expected waiting time is simply the reciprocal of the total probability mass of L (expectation of the geometric distribution). We’ve
This gives our waiting time (the expected number of characters typed before the monkey hits upon a legal regular expression)
Exercise 8.41
000 is contained within the bitstring 00011. Finally, nonvoid external nodes are shown with full bitstrings for clarity, although remaining suffixes could work, too.When altering a data structure to accommodate additional cases, like handling bitstrings that aren’t anymore prefix-free, the resulting structure may conflict with the original definition. In this case, we do allow void and nonvoid external nodes to be siblings for red internal nodes.
Exercise 8.42
Exercise 8.43
Assume that the same structure means the same arrangement of internal nodes. This entails that leaves must be associated with 2 nonvoid external nodes. We can vary how the remaining void external nodes may turn into nonvoid ones.
The leftmost trie has ∑k=04(k4)=24=16 configurations. The middle has 28=256 configurations. The rightmost trie is full, so the given configuration is unique.
Exercise 8.44 🌟
This exercise expands the book by providing a full derivation of the OGF that enumerates tries with N external nodes.
For any given trie t with N external nodes, we can have 2N−2leaves(t) configurations, where leaves(t) counts the number of leaves in t (see also the previous exercise). To generalize the enumeration for any N (in the spirit of Figure 8.6 in the book) we should derive the corresponding BGF.
The book already defines T(z,u) (see Chapter 5 or the book’s website) such that z tracks internal nodes and u tracks leaves. For a tree with N external nodes, there are exactly N−1 internal nodes. Our combinatorial rule states that for a given tree with N external nodes and k leaves, the number of configurations is 2N−2k=2N(1/4)k. Thus, we can find the total number of trie configurations by evaluating T(z,u) at u=1/4 and multiplying the zN−1 coefficient by 2N. Let's define a raw generating function Y(z) that applies this transformation
We need to check the boundary condition for N=1. The empty tree in T(z,u) is represented by 1. Thus,
It turns out, that it reports 2 tries, whereas from Figure 8.6 we have X1=1. The discrepancy emanates from Y(z) being discriminatory regarding void and nonvoid external nodes. To perfectly match X(z) to Figure 8.6, we simply subtract that extra empty-set configuration
Now, we’re left with truly mechanical work to find the closed-form equation. We can execute the algebra starting directly from the book's equation
After applying substitutions, multiplying by the previously mentioned factor, solving the quadratics for Y(z), and finally subtracting z, we get (the algebraic steps are omitted to keep the exposition tidy)
Using WolframAlpha we can expand the OGF to see the first few terms
It perfectly matches Figure 8.6 and, in principle, we can find any coefficient. We can also find the growth rate of coefficients by transforming X(z)
Now, we can directly apply the corollary of Theorem 5.5 (see the book's website) with ρ=1/6 and α=−1/2 to get
Exercise 8.45 🌟
This exercise continues expanding the book's content to find the proportion of the external nodes that are void in a “random” trie (assuming each different trie structure to be equally likely to occur).
Essentially, this is a variation of the previous exercise. To track the void nodes, we want a BGF X(z,v) where z marks all external nodes and v marks only the void ones. In a trie with N external nodes and k leaves, N−2k external nodes are toggleable. Each can be nonvoid (weight 1) or void (weight v), contributing a factor of (1+v). In the previous exercise this was 2, since we didn’t care to further classify them. The polynomial tracking the configurations of this specific shape is
Factoring out the term raised to N−1 power is necessary to reuse T(z,u) from the book, which tracks internal nodes under z.
Now, the derivation virtually follows the steps from the previous exercise. We have
The boundary condition here requires us to subtract zv, since an empty trie is a single nonvoid external node. Solving the equation gives
Let C(z) be the CGF for the total number of void nodes across all tries of size N (see Table 3.5 in the book)
The expected proportion of void nodes in a random trie of size N is
The above estimate follows by employing the corollary of Theorem 5.5 on C(z)
We already know from before the estimate of X(z,1).
Exercise 8.46
Let N be the number of strings in the set (which equals the number of nonvoid external nodes). Let I be the number of distinct prefixes (including the empty prefix ϵ) shared by at least two strings in the set (which equals the total number of internal nodes, because every shared prefix forces a split). We also know that in any binary tree (including tries) the number of external nodes is I+1. Hence, the number of void external nodes is simply I+1−N. Consequently, we have a void external node if this amount is positive.
Testing this formula on the set of strings associated with the tries from Figure 8.5 in the book perfectly gives back the number of external void nodes. For computing the formula without building a trie, we can leverage the longest common prefix for strings. Namely, we would register all LCPs between adjacent pair of strings in a previously sorted list. The cardinality of the set of these LCPs gives back the value for I.
Exercise 8.47
If we use 1-based indexing then the last position to be examined is 29, since both patterns are 10 bits long. To compute the total number of bits examined, it’s best to group distinct prefixes that occur at all possible positions and handle them separately. For example, a single 0 entails 1 bit comparison to hit a void external node. 11 needs 2 bit comparisons, since both patterns start with 10 (the mismatch happens at the second bit). The total sum is 73 bit comparisons.
Exercise 8.48
The basic variant revolves around searching for multiple patterns in a text using a trie. We first build a trie from the set of pattern strings. Set the counter to zero. For every valid position in the text perform a search and increase the counter whenever a nonvoid external node is encountered.
The previous algorithm assumes a set of prefix-free patterns. Otherwise, the algorithm just needs a slight tweak to increment the counter if the search passes through a valid prefix marker (see Exercise 8.41) or terminates at a nonvoid external node.
The improved version would use an altered Aho-Corasick FSA, where we simply need to treat the accepting states exactly like the internal states. Every visit to the corresponding accept state would also increment the counter. Figure 8.8 in the book shows a halting machine.
Exercise 8.49
Exercise 8.50
Exercise 8.51 🌟
This exercise introduces the concept of an output link in the Aho-Corasick FSA for handling overlapping prefixes.
Exercise 8.52 🌟
This exercise provides a detailed derivation of the mean number of internal nodes in a trie corresponding to N random bitstrings.
We proceed as in the proof of the corollary of Theorem 4.10 in the book. The recurrence can be simplified to
Multiplying by zN/N! and summing on N leaves a straightforward convolution where the EGF ∑N≥0ANzN/N! must satisfy the functional equation
Iterating the equation, we find that
Using Table 3.4 from the book, we get
Now, we proceed exactly as in the proof of Theorem 8.8 by first approximating the sum
We split this sum at j=⌊lgN⌋. Let's call this boundary L=⌊lgN⌋. By shifting the index of summation below j→j+L, the bounds of our two sums become −L≤j<0 and j≥0.
Because L=⌊lgN⌋ as N→∞, the boundary L grows infinitely large. The terms added by extending the lower bound from −L to −∞ are exponentially small and decay so rapidly that their sum introduces a negligible error of o(1). Therefore, we can safely complete the tail to form a single, doubly-infinite sum.
In the derivation, we’ve used the following equations:
Our sum is essentially of the form P(N)=∑j=−∞∞Q({lgN}−j), where
Observe that P(N) is a periodic function, since P(2N)=P(N). By the laws of Fourier analysis, any periodic function can be represented as its mean value plus a fluctuating wave. This gives
We’ve used the substitution u=2x, so du=2xln2⋅dx. The integral can be evaluated in WolframAlpha resulting in 1. Therefore,
Exercise 8.53
The next Python script computes the values of Q(x) using Numpy and produces the plot shown below.

Exercise 8.54 🌟
This exercise introduces the Poisson transform to simplify analytic derivations. See also Exercise 8.57 for a detailed overview.
We start by establishing a new functional equation
Using Table 3.4 from the book, we get
There is a typo in the book, we need to find C^N=N![zN]C^(z).
This gives
Apply the binomial sum operation to get
Exercise 8.55
We start with the expression from Theorem 8.8
Using the standard binomial expansion, we have
Substitute this expansion back into the 1−(…) part of our original equation. The leading 1s cancel out, so
We want our sum to look like the one given in the book, which uses an index k running from 2 to N. Let k=m+1 (which means m=k−1). If m goes from 1 to N−1, then k goes from 2 to N. Substituting all of this back, and applying the "absorption" identify N(k−1N−1)=k(kN), gives us
Because these are finite sums for any given N (and the infinite tail converges), we can safely swap the order of summation. This gives
The inner sum is a standard, converging infinite geometric series (recall that k≥2)
Substitute this collapsed fraction back into the outer sum
Exercise 8.56
We start with the recurrence for N≥2
Multiply by 2N to avoid divisions
Let R(z)=∑N≥0RNN!zN be the target EGF. It satifies the functional equation
Just like Exercise 8.54, we apply the Poisson transform by substituting R^(z)≡e−zR(z). This gives
Substituting z→z/2 and iterating the recurrence down to zero gives the explicit infinite sum
We need to find R^k=k![zk]R^(z). Letting c=1/2j, we extract the kth coefficient for k≥2 (the constant 1 drops out)
Now, apply the EGF binomial sum operation to get
To get this into a form we can approximate, we expand the denominator back into an infinite geometric series and swap the order of summation
Let's evaluate that inner sum. We start with the binomial expansion
Notice that the sum we want is exactly x⋅g′(x)−g(x):
x⋅g′(x)=x[−N(1−x)N−1+N]=Nx−Nx(1−x)N−1
Subtract g(x): Nx−Nx(1−x)N−1−[(1−x)N−1+Nx]
This gives
Substitute x=2−j back into our outer sum to get the exact formula for RN
Apply the approximation from the book
if we split the sum at j=⌊lgN⌋, the summation over the 1s yields exactly ⌊lgN⌋. The remaining decaying fractional tails evaluate to a fixed constant, plus the microscopic oscillating wave. Therefore,
Exercise 8.57
We are given the recurrence for N>1
with initial conditions p0=0 and p1=1.
Let P(z)=∑N≥0pNN!zN. The RHS is the standard binomial sum P(z)ez, whilst the LHS generates P(2z). However, we must correct for the N=1 boundary condition. Looking at the coefficients of z1:
LHS: 21p1=2
RHS: 1ez)=p0+p1=1
To balance the equation, we must add z to the right side. Our exact functional equation is
Now, apply the Poisson transform P^(z)≡e−zP(z) by multiplying the whole equation by e−2z
Substitute z→z/2 and iterate the equation down to 0
This gives
Now, we apply the binomial sum operation to read out
To get this into a form we can approximate, we proceed exactly as in the previous exercise. Thus,
The inner sum over k is exactly x⋅g′(x) for the binomial expansion g(x)=(1−x)N, evaluated at x=2−j
Substitute x=2−j back in to get the exact closed form
Apply the exponential approximation from the book. We also extend the boundaries in both directions, knowing that negative indices produce exponentially small quantities.
We are summing a function f(x)=xe−x evaluated at exponentially spaced points xj=N/2j. Switching to a logarithmic scale y=log2x creates equidistant points for applying the Riemann integral. In other words,
Therefore,
The microscopic ±10−5 oscillating term ϵ^(N) is hiding in the difference between the discrete sum over the fractional part {lgN} and this continuous integral mean, exactly as we proved in the internal nodes derivation.
Exercise 8.58
The extended version of Prodinger’s algorithm is completely described in the paper The Swedish Leader Election Protocol: Analysis and Variations. In the analysis, the authors use the Poisson transform technique that’s exemplified in several previous exercises. The new algorithm introduces an additional parameter τ that controls the allowed number of consecutive failed (null) rounds.
The classical (fair-coin leader election approach) RN=lgN+O(1) bound naturally falls out of their formula as τ→∞ and p=q=1/2.
Exercise 8.59
In the analysis below, we assume that the algorithm terminates once the run is found (early stoppage).
Let N be the total length of the text string. The algorithm checks positions M,2M,3M,…,N. This gives us N/M total checkpoints.
To determine the average number of bits examined by this algorithm, we can break the problem down into two distinct scenarios: what happens at a standard checkpoint in the random text, and what happens when the algorithm hits the hidden run of M zeros.
At a typical checkpoint kM located in the random text, the algorithm checks t bits. Let W be the random variable for the number of bits read in a window (initial scan). Its probability mass function is
We’ve
We know that P(W≥i)=(21)i−1, since after successfully reading i−1 bits, we’ll surely read one more. This gives
With probability 2−t, all t bits in the window are 0. When this happens, the algorithm checks bits on the left and right to determine the run length. Because the surrounding text is random, the expected number of bits checked to the left until a 1 is found is exactly 2 (geometric distribution). The expected number to the right is also 2. This adds an expected 4 bits to our check.
The total expected number of bits examined per random checkpoint is 2+21−t.
There is exactly one checkpoint that falls inside the hidden run of M zeros. Since the problem states that M≫t, we can safely ignore the O(t/M) edge case where the t-bit window overlaps the boundary of the hidden run. Therefore, the t bits checked will entirely consist of 0s.The algorithm will then expand to the left and the right. It will successfully read all the remaining zeros in the run, and it will only stop when it hits the 1 immediately to the left of the run and the 1 immediately to the right. Because it reads the entire hidden run plus exactly two bounding 1s, the total number of bits examined for this specific checkpoint is exactly M+2.
Because the hidden run is uniformly distributed, the algorithm will, on average, check (N/M+1)/2 checkpoints before finding it. Among these 1 is a successful search and the others are failures. This gives
Exercise 8.60
The key is to adapt the skipping mechanic from the previous exercise by making the skip distance dynamic. Instead of searching for a fixed run of length M, we use our "longest run found so far" to dictate how far we can safely jump ahead.
Let L be the length of the longest run of 0s found so far. We initialize it to zero. The algorithm is outlined below.
Dynamic Probing
Because we only care about finding a run strictly longer than our current record L, any new record-breaking run must contain at least L+1 consecutive 0s. If we are starting a search from position i, we do not read bit i (except when L=0). Instead, we skip ahead and directly probe the bit at position i+L.
Bit is 1
If the bit at i+L is a 1, it is physically impossible for a run of L+1 zeros to start anywhere between i and i+L, because that 1 would interrupt it! We can safely discard this entire block without reading any of the intermediate bits. We move our next position to i=i+L+1 and repeat the probing.
Bit is 0
If the bit at i+L is a 0, a record-breaking run might exist here. Just like the previous exercise, we read backward toward i.
If we hit a 1 while reading backward, it's a false alarm. We shift our search window past that 1 and resume probing.
If we read all the way back to i and they are all 0s, we have a new record! We then read forward from i+L+1 to see exactly how long this new run is. At any rate, we also update L to reflect this longest run of 0s as well as set our new position to continue the search after this block.
We know from the book, that in a random bitstring of length N, the expected length of the longest run of zeros is roughly lgN. By the time the algorithm is halfway through the text, L will likely be close to lgN. This is a significant improvement compared to a naive algorithm that maintains a simple counter and reads all bits of a string. This means our algorithm operates in O(N/lgN) expected time.
Last updated