100% found this document useful (5 votes)
22 views

Full download Programming Interview Problems: Dynamic Programming (with solutions in Python) 1st Edition Leonardo Rossi pdf docx

Interview

Uploaded by

bakkosalhiui
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (5 votes)
22 views

Full download Programming Interview Problems: Dynamic Programming (with solutions in Python) 1st Edition Leonardo Rossi pdf docx

Interview

Uploaded by

bakkosalhiui
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 65

Download the Full Version of textbook for Fast Typing at textbookfull.

com

Programming Interview Problems: Dynamic


Programming (with solutions in Python) 1st Edition
Leonardo Rossi

https://textbookfull.com/product/programming-interview-
problems-dynamic-programming-with-solutions-in-python-1st-
edition-leonardo-rossi/

OR CLICK BUTTON

DOWNLOAD NOW

Download More textbook Instantly Today - Get Yours Now at textbookfull.com


Recommended digital products (PDF, EPUB, MOBI) that
you can download immediately if you are interested.

Discovering Computer Science Interdisciplinary Problems


Principles and Python Programming 1st Edition Jessen
Havill
https://textbookfull.com/product/discovering-computer-science-
interdisciplinary-problems-principles-and-python-programming-1st-
edition-jessen-havill/
textboxfull.com

Discovering Computer Science Interdisciplinary Problems


Principles and Python Programming First Edition Jessen
Havill
https://textbookfull.com/product/discovering-computer-science-
interdisciplinary-problems-principles-and-python-programming-first-
edition-jessen-havill/
textboxfull.com

Discovering Computer Science: Interdisciplinary Problems,


Principles, and Python Programming 2nd Edition Jessen
Havill
https://textbookfull.com/product/discovering-computer-science-
interdisciplinary-problems-principles-and-python-programming-2nd-
edition-jessen-havill/
textboxfull.com

Discovering Computer Science Interdisciplinary Problems


Principles and Python Programming 2nd Edition Jessen
Havill
https://textbookfull.com/product/discovering-computer-science-
interdisciplinary-problems-principles-and-python-programming-2nd-
edition-jessen-havill-2/
textboxfull.com
Cracking C Programming Interview 500 interview questions
and explanations to sharpen your C concepts for a
lucrative programming career English Edition Jhamb
https://textbookfull.com/product/cracking-c-programming-
interview-500-interview-questions-and-explanations-to-sharpen-your-c-
concepts-for-a-lucrative-programming-career-english-edition-jhamb/
textboxfull.com

Robust Adaptive Dynamic Programming 1st Edition Hao Yu

https://textbookfull.com/product/robust-adaptive-dynamic-
programming-1st-edition-hao-yu/

textboxfull.com

Python Network Programming Cookbook Kathiravelu

https://textbookfull.com/product/python-network-programming-cookbook-
kathiravelu/

textboxfull.com

Python GUI Programming Cookbook Meier

https://textbookfull.com/product/python-gui-programming-cookbook-
meier/

textboxfull.com

Abstract Dynamic Programming Second Edition Dimitri P.


Bertsekas

https://textbookfull.com/product/abstract-dynamic-programming-second-
edition-dimitri-p-bertsekas/

textboxfull.com
1

Programming Interview Problems: Dynamic Programming (with solutions in Python)


Copyright 2020 Leonardo Rossi
The designations of products and technologies that appear in this book may be claimed as
trademarks by their manufacturers and sellers.
While every precaution has been taken in the preparation of this book, the publisher and
authors assume no responsibility for errors or omissions, or for damages resulting from the
use of the information contained herein.
2 Contents

Contents

Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
General advice for the interview . . . . . . . . . . . . . . . . . . . . . . . 6
1 The Fibonacci sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Solution 1: brute force, 𝑂(2𝑛 ) time 7
Solution 2: dynamic programming, top-down 9
Solution 2: dynamic programming, bottom-up 11
2 Optimal stock market strategy . . . . . . . . . . . . . . . . . . . . . . . . 13
Solution 1: dynamic programming, top-down, 𝑂(𝑛) time 13
Solution 2: dynamic programming, bottom-up, 𝑂(𝑛) time 15
Variation: limited investment budget 16
Variation: limited number of transactions 17
3 Change-making . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
Clarification questions 20
Solution 1: dynamic programming, top-down, 𝑂(𝑛𝑣) time 20
Solution 2: dynamic programming, bottom-up, 𝑂(𝑛𝑣) time 25
Solution 3: dynamic programming + BFS, bottom-up, 𝑂(𝑛𝑣) time 26
Variant: count the number of ways to pay (permutations) 29
Solution: dynamic-programming, top-down, 𝑂(𝑛𝑣) 29
Variant: count the number of ways to pay (combinations) 32
Solution: dynamic-programming, top-down, 𝑂(𝑛𝑣) 32
4 Number of expressions with a target result . . . . . . . . . . . . . . . . . . 36
Clarification questions 36
Solution 1: brute-force, 𝑂(2𝑛 ) time 36
Solution 2: dynamic programming, top-down, 𝑂(𝑛𝑆) time 38
Solution 3: dynamic programming + BFS, bottom-up, 𝑂(𝑛𝑆) time 39
Unit tests 41
5 Partitioning a set into equal-sum parts . . . . . . . . . . . . . . . . . . . . 43
Clarification questions 43
Solution 1: dynamic programming, top-down, 𝑂(𝑛𝑆) time 43
6 Splitting a string without spaces into words . . . . . . . . . . . . . . . . . . 46
Clarification questions 46
Solution 1: dynamic programming, top-down, 𝑂(𝑛𝑤) time 46
Solution 2: dynamic programming + BFS/DFS, bottom-up, 𝑂(𝑛𝑤) time 48
7 The number of binary search trees . . . . . . . . . . . . . . . . . . . . . . 50
Solution 1: dynamic programming, top-down, 𝑂(𝑛2 ) time 50
8 The maximum-sum subarray . . . . . . . . . . . . . . . . . . . . . . . . 55
Clarification questions 55
Solution 1: dynamic programming, 𝑂(𝑛) time, 𝑂(𝑛) space 55
Solution 2: dynamic programming, 𝑂(𝑛) time, 𝑂(1) space 61
Unit tests 61
9 The maximum-product subarray . . . . . . . . . . . . . . . . . . . . . . . 63
Clarification questions 63
Solution 1: greedy, two-pass, 𝑂(𝑛) time 63
Contents 3

Solution 2: dynamic programming, one-pass, 𝑂(𝑛) time 69


Unit tests 73
10 Shortest pair of subarrays with target sum . . . . . . . . . . . . . . . . . . 75
Clarification questions 75
Solution 1: dynamic programming + sliding window, 𝑂(𝑛) time, 𝑂(𝑛) space 75
11 Longest palindromic substring . . . . . . . . . . . . . . . . . . . . . . . . 81
Clarification questions 81
Solution 1: brute force, 𝑂(𝑛3 ) 81
Checking if a string is a palindrome 81
Checking if a string is a palindrome: a faster way 83
Putting it all together 83
Solution 2: dynamic programming, 𝑂(𝑛2 ) 84
Solution 3: dynamic programming, 𝑂(𝑛) 88
Unit tests 90
12 Longest valid parentheses substring . . . . . . . . . . . . . . . . . . . . . 92
Clarification questions 92
Solution 1: dynamic programming, bottom-up, 𝑂(𝑛) 92
Solution 3: dynamic programming, top-down, 𝑂(𝑛) 97
Unit tests 98
13 Longest increasing subsequence . . . . . . . . . . . . . . . . . . . . . . . 100
Clarification questions 100
Solution 1: dynamic programming, bottom-up, 𝑂(𝑛2 ) time, 𝑂(𝑛2 ) space 100
Solution 2: dynamic programming, bottom-up, 𝑂(𝑛2 ) time, 𝑂(𝑛) space 103
Variant: count the number of solutions 105
Solution: dynamic programming, bottom-up, 𝑂(𝑛2 ) time, 𝑂(𝑛) space 105
14 Longest arithmetic subsequence . . . . . . . . . . . . . . . . . . . . . . . 108
Clarification questions 108
Solution 1: dynamic programming, bottom-up, 𝑂(𝑛2 ) time, 𝑂(𝑛2 ) space 108
Unit tests 112
15 Dealing the best hand of cards . . . . . . . . . . . . . . . . . . . . . . . . 114
Clarification questions 114
Solution 1: brute force, 𝑂(𝑛2 ) time 114
Solution 2: dynamic programming, 𝑂(𝑛) time 115
Unit tests 116
16 Number of ways to climb stairs . . . . . . . . . . . . . . . . . . . . . . . 118
Clarification questions 118
Solution 1: dynamic programming, top-down, 𝑂(𝑛) time 118
Solution 2: dynamic programming, bottom-up, 𝑂(𝑛) time 119
Solution 3: dynamic programming, bottom-up, 𝑂(1) time 119
Unit tests 120
17 Number of paths through maze . . . . . . . . . . . . . . . . . . . . . . . 121
Clarification questions 121
Solution 1: dynamic programming, top-down, 𝑂(𝑛2 ) time 121
Solution 2: dynamic programming, bottom-up, 𝑂(𝑛2 ) time 124
Solution 3: dynamic programming, bottom-up, 𝑂(𝑛2 ) time, linear space 125
4 Contents

Unit tests 126


18 Maximum-score path through maze . . . . . . . . . . . . . . . . . . . . . 129
Clarification questions 129
Solution 1: dynamic programming, top-down, 𝑂(𝑛2 ) time 129
Solution 2: dynamic programming, bottom-up, 𝑂(𝑛2 ) time 132
Solution 3: dynamic programming, bottom-up, 𝑂(𝑛2 ) time, linear space 133
Unit tests 134
19 Subarray sum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Clarification questions 137
Solution 1: brute-force, 𝑂(𝑚𝑛) 137
Solution 2: dynamic programming, 𝑂(𝑚 + 𝑛) 137
20 Submatrix sum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
Clarification questions 139
Solution 1: brute-force, 𝑂(𝑚𝑛2 ) 139
Solution 2: dynamic programming, 𝑂(𝑚 + 𝑛2 ) 140
Unit tests 144
21 Largest square submatrix of ones . . . . . . . . . . . . . . . . . . . . . . . 148
Clarification questions 148
Solution 1: brute-force, 𝑂(𝑛5 ) 148
Solution 2: dynamic programming, 𝑂(𝑛2 ) 150
22 Largest rectangle in skyline . . . . . . . . . . . . . . . . . . . . . . . . . 154
Clarification questions 154
Solution 1: brute-force, 𝑂(𝑛3 ) 154
Solution 2: dynamic programming, 𝑂(𝑛2 ) 155
Solution 3: dynamic programming + stack, 𝑂(𝑛) 156
23 Largest submatrix of ones . . . . . . . . . . . . . . . . . . . . . . . . . . 162
Clarification questions 162
Solution 1: brute-force, 𝑂(𝑛6 ) 162
Solution 2: dynamic programming, 𝑂(𝑛3 ) 163
Solution 3: dynamic programming, 𝑂(𝑛2 ) 166
24 Interleaved strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
Clarification questions 168
Solution 1: brute-force, 𝑂(2𝑛 ) 168
Solution 2: dynamic programming, 𝑂(𝑛2 ) 172
25 Regular expression matching . . . . . . . . . . . . . . . . . . . . . . . . 174
Clarification questions 174
Solution 1: brute-force, 𝑂(2𝑛 ) 174
Solution 2: dynamic programming, 𝑂(𝑛2 ) 178
Unit tests 180
Contents 5

Preface
Over the last decade, many companies have adopted the FANG-style interview process for
software engineer positions: programming questions that involve an algorithm design step,
and often require competitive programming solving techniques.
The advantages and disadvantages of this style of interview questions are highly debated, and
outside the scope of this book. What is important is that the questions that are asked pose
serious challenges to candidates, thus require thorough preparation.
The class of problems that are by far the most challenging is dynamic programming. This is
due to a combination of dynamic programming being rarely used in day-to-day work, and the
difficulty of finding a solution in a short amount of time, when not having prior experience
with this method.
This book presents 25 dynamic programming problems that are most commonly encoun-
tered in interviews, and several of their variants. For each problem, multiple solutions are
given, including a gradual walkthrough that shows how one may reach the answer. The goal
is not to memorize solutions, but to understand how they work and learn the fundamental
techniques, such that new, previously unseen problems can be solved with ease.
The solutions are very detailed, showing example walkthrougs, verbal explanations of the
solutions, and many diagrams and drawings. These were designed in a way that helps both
verbal and visual learners grasp the material with ease. The code implementation usually
comes last, accompanied by unit tests and complexity/performance analysis.
A particular focus has been put on code clarity and readability: during the interview, we are
expected to write code as we would on the job. This means that the code must be tidy, with
well-named variables, short functions that do one thing, modularity, comments describing
why we do certain things etc. This is, sadly, unlike most other material on dynamic pro-
gramming that one may find online, in competitive programming resources, or even in well-
known algorithm design textbooks. In fact, the poor state of the material on this topic is the
main reason I wrote this book.
I hope that you find this book useful for preparing to get the job you wish for. Whether you
like the book or not, I would appreciate your feedback through a book review.
Good luck with your interviews!
6 Contents

General advice for the interview


Find out in advance what kind of questions will be asked. Usually, they are in one or more
of the following categories: coding, algorithm design (with or without implementation),
domain-specific questions, theoretical questions, behavioral questions, live coding, debug-
ging, one-day assignments or pair programming. You should know what you need to prepare
for.
For programming questions, do not jump directly to writing code, even if the solution looks
obvious. First, ask clarification questions and go over some simple examples, to make sure
you understand the requirements well. Then, you may sketch a solution and discuss it with
the interviewer. Several iterations might be needed in case of correctness or performance
issues. Only once they agree that the solution looks fine, proceed with implementing it.
Treat the interview questions not like problems, but real job assignments. Take them seriously
and solve them thoroughly, like a professional.
Treat your interviewers as colleagues, with whom you must work together to solve the as-
signment. Shift into the mindset that you are already on the same team. Discuss and interact
with them the same way you would with colleagues on the job.
Write tidy, clean code—as much as possible given the time you have. Try to follow typi-
cal coding style guidelines for your language. For Python, you can find good guidelines at
www.python.org/dev/peps/pep-0008 and google.github.io/styleguide/pyguide.html.
Do not forget to ask about the size of the data your code needs to handle—this is important
to determine the time complexity of your solution.
If you cannot find the fastest algorithm needed to solve the problem, propose a slower one.
Sometimes you might get an idea (or a hint from the interviewer) to make it faster once you
write it. But even if you do not, any solution is better than no solution.
Try to be quick in your answer. Interviewers usually plan to ask follow-up questions. If you
do not have sufficient time left for these, your result may be poor, even if you answered the
original question well.
7

1 The Fibonacci sequence


Return the n-th number in the Fibonacci sequence. The first two numbers in the Fibonacci
sequence are equal to 1; any other number is equal to the sum of the preceding two numbers.
Example: for n = 6, the first 6 numbers of the sequence are [1, 1, 2, 3, 5, 8] so the result
is 8.

Solution 1: brute force, 𝑂(2𝑛 ) time


A straightforward solution is to implement the function recursively:
def fibonacci(n):
if n <= 2:
return 1
return fibonacci(n ­ 1) + fibonacci(n ­ 2)

The above code is correct but too slow due to redundancies. We can see this if we add logging
to the function:
import inspect
def stack_depth():
return len(inspect.getouterframes(inspect.currentframe())) ­ 1

def fibonacci(n):
print("{indent}fibonacci({n}) called".format(
indent=" " * stack_depth(), n=n))
if n <= 2:
return 1
return fibonacci(n ­ 1) + fibonacci(n ­ 2)

fibonacci(6)

We changed the code to print the argument passed to the fibonacci function. The message
is indented by the call stack depth, so that we can see better which function call is causing
which subsequent calls. Running the above code prints:
fibonacci(6) called
fibonacci(5) called
fibonacci(4) called
fibonacci(3) called
fibonacci(2) called
fibonacci(1) called
fibonacci(2) called
fibonacci(3) called
fibonacci(2) called
fibonacci(1) called
fibonacci(4) called
8 1 The Fibonacci sequence

fibonacci(3) called
fibonacci(2) called
fibonacci(1) called
fibonacci(2) called

That’s a lot of calls! If we draw the call graph, we can see that it’s an almost full binary tree:
9

Notice that the height of the binary tree is n (in this case, 6). The tree is almost full, thus
it has 𝑂(2𝑛 ) nodes. Since each node represends a call of our function, our algorithm has
exponential complexity.

Solution 2: dynamic programming, top-down


We can optimize the previous solution by avoiding redundant computations. These redun-
dancies are visible in the call graph:

• fibonacci(4) is called twice, once by fibonacci(5) and once by fibonacci(6);


• fibonacci(3) is called 3 times;
• fibonacci(2) is called 5 times;
• fibonacci(1) is called 3 times.

It does not make sense to compute, for example, the 4-th Fibonacci number twice, since it
does not change. We should compute it only once and cache the result.

Let’s use a dictionary to store the cache:


fibonacci_cache = {}
def fibonacci(n):
if n <= 2:
return 1
if n not in fibonacci_cache:
fibonacci_cache[n] = fibonacci(n ­ 1) + fibonacci(n ­ 2)
return fibonacci_cache[n]

The call graph of the optimized code looks like this:

Notice how only the first call to fibonacci(n) recurses. All subsequent calls return from
the cache the value that was previously computed.

This implementation has 𝑂(𝑛) time complexity, since exactly one function call is needed to
compute each number in the series.
10 1 The Fibonacci sequence

This strategy of caching the results of subproblems is called dynamic programming.

While the above code is correct, there are some code style issues:

• We introduced the global variable fibonacci_cache; it would be great if we could


avoid global variables, since they impact code readability;
• The code is more complicated than before due to the cache manipulations.

We can avoid adding the global variable by using instead an attribute called cache that is
attached to the function:
def fibonacci(n):
if n <= 2:
return 1
if not hasattr(fibonacci, 'cache'):
fibonacci.cache = {}
if n not in fibonacci.cache:
fibonacci.cache[n] = fibonacci(n ­ 1) + fibonacci(n ­ 2)
return fibonacci.cache[n]

The advantage is that the cache variable is now owned by the function, so no external code
is needed anymore to initialize it. The disadvantage is that the code has become even more
complicated, thus harder to read and modify.

A better approach is to keep the original function simple, and wrap it with a decorator that
performs the caching:
def cached(f):
cache = {}
def worker(*args):
if args not in cache:
cache[args] = f(*args)
return cache[args]
return worker

@cached
def fibonacci(n):
if n <= 2:
return 1
return fibonacci(n ­ 1) + fibonacci(n ­ 2)

The good news is that Python 3 has built-in support for caching decorators, so there is no
need to roll your own:
from functools import lru_cache

@lru_cache(maxsize=None)
def fibonacci(n):
if n <= 2:
11

return 1
return fibonacci(n ­ 1) + fibonacci(n ­ 2)

By default lru_cache is limited to 128 entries, with least-recently used entries evicted when
the size limit is hit. Passing maxsize=None to lru_cache ensures that there is no memory
limit and all values are cached. In practice, it might be preferrable to set a limit instead of
letting memory usage increase without bounds.

Prefer standard library functionality to rolling your own


Using the standard lru_cache decorator makes the code easier to read, since it has well-
known behavior. The advange over using a custom caching method is that the reader
does not need to spend time to understand its details.

Solution 2: dynamic programming, bottom-up


While computing the Fibonacci sequence recursively is useful for pedagogical reasons, it is
more intuitive to compute it iteratively starting from the smaller numbers, just like a human
would do:
def fibonacci(n):
series = [1, 1]
while len(series) < n:
series.append(series[­1] + series[­2])
return series[­1]

The code has 𝑂(𝑛) time complexity, as well as 𝑂(𝑛) space complexity. In practice the perfor-
mance is better than the recursive implementation, since there is no overhead due to extra
function calls.

The space complexity can be reduced to 𝑂(1) if we notice that we do not need to store the
entire sequence, just the last two numbers:
def fibonacci(n):
previous = 1
current = 1
for i in range(n ­ 2):
next = current + previous
previous, current = current, next
return current

We have written an algorithm that starts from the smallest subproblem (the first two numbers
in the sequence), then expands the solution to reach the original problem (the n-th number
in the sequence). This approach is called bottom-up dynamic programming. By contrast,
the previous approach of solving the problem recursively starting from the top is called top-
down dynamic programming. Both approaches are equally valid; one or the other may be
more intuitive, depending on the problem.
12 1 The Fibonacci sequence

In the rest of the book, we will look at how we can apply dynamic programming to solving
non-trivial problems. In general, we will show both top-down and bottom-up solutions. We
will see that the top-down approach is often easier to understand and implement, however it
offers less optimization opportunities compared to bottom-up.
13

2 Optimal stock market strategy


When evaluating stock market trading strategies, it is useful to determine the maximum pos-
sible profit that can be made by trading a certain stock. Write an algorithm that, given the
daily price of a stock, computes the maximum profit that can be made by buying and selling
that stock. Assume that you are allowed to own no more than 1 share at any time, and that
you have an unlimited budget.
Example 1: The stock price over several days is [2, 5, 1]. The best strategy is to buy a share
on the first day for price 2, then sell it on the second day for price 5, obtaining a profit of 3.
Example 2: The stock price over several days is [2, 5, 1, 3]. The best strategy is to buy a
share on the first day for price 2, then sell it on the second day for price 5, obtaining a profit
of 3; then buy it again on the third day for price 1, and sell it on the fourth day for price 3,
obtaining an overall profit of 5.

Solution 1: dynamic programming, top-down, 𝑂(𝑛) time


The first idea that comes to mind while approaching this problem is using a state machine.
This is because on any day, our state can be described by:
• whether we own the share or not;
• the amount of money we have.
Between the states of consecutive days, we have only four possible transitions:
• If at the end of the previous day we did not own the share:
• buying the stock, so we now own it, but we have less money;
• avoiding the stock, so we keep our money unchanged;
• If at the end of the previous day we owned the share:
• selling the stock, so we no longer own it, and have more money;
• holding the stock, so we keep both the stock and our money unchanged.

Knowing this, we can model the entire problem using a state machine. In our initial state,
14 2 Optimal stock market strategy

we have some amount of cash and no shares. In the final state, we have some other amount
of cash (ideally higher), and no shares. In between, we have state transitions:

Solving the original problem can be reduced to finding a chain of transitions through this
state machine, that yields the maximum profit.
Notice how our state during any day only depends on the state from the previous day. This is
excellent: we can express our problem using a simple recurrence relation, just as we did for
the Fibonacci sequence problem.
The structure of the solution using a recursive algorithm looks like this:
def max_profit(daily_price):
def get_best_profit(day, have_stock):
"""
Returns the best profit that can be obtained by the end of the day.
At the end of the day:
* if have_stock is True, the trader must own the stock;
* if have_stock is False, the trader must not own the stock.
"""
# TODO ...
# Final state: end of last day, no shares owned.
last_day = len(daily_price) ­ 1
no_stock = False
return get_best_profit(last_day, no_stock)

Note that we defined a helper function get_best_profit which takes as parameters the
identifiers of a state: the day number and whether we own the stock or not at the end of the
day. We use get_best_profit to compute the profit for a specific state in the state machine.
Let’s now implement the helper using a recurrence relation. We need to consider the previous
states that can transition into the current state, and choose the best one:
@lru_cache(maxsize=None)
def get_best_profit(day, have_stock):
"""
15

Returns the best profit that can be obtained by the end of the day.
At the end of the day:
* if have_stock is True, the trader must own the stock;
* if have_stock is False, the trader must not own the stock.
"""
if day < 0:
if not have_stock:
# Initial state: no stock and no profit.
return 0
else:
# We are not allowed to have initial stock.
# Add a very large penalty to eliminate this option.
return ­float('inf')
price = daily_price[day]
if have_stock:
# We can reach this state by buying or holding.
strategy_buy = get_best_profit(day ­ 1, False) ­ price
strategy_hold = get_best_profit(day ­ 1, True)
return max(strategy_buy, strategy_hold)
else:
# We can reach this state by selling or avoiding.
strategy_sell = get_best_profit(day ­ 1, True) + price
strategy_avoid = get_best_profit(day ­ 1, False)
return max(strategy_sell, strategy_avoid)

The first part of the helper implements the termination condition, i.e. handling the initial
state, while the second part implements the recurrence. To simplify the logic of the recur-
rence we allow selling on any day including the first, but we ensure that selling on the first
day would yield a negative profit, so it’s an option that cannot be chosen as optimal.

Handle the termination condition early


It is preferrable to handle the termination condition of a recursive function in a single
place, as opposed to wrapping each call of the function with a check like if day < 0
.... Handling it early simplifies greatly the logic and makes the code easier to read.

Both the time and space complexity of this solution are 𝑂(𝑛). Note that it is important to
cache the results of the helper function, otherwise the time complexity becomes exponential
instead of linear.

Solution 2: dynamic programming, bottom-up, 𝑂(𝑛) time


Once we have implemented the top-down solution, it is easy to rewrite it as bottom-up: we
start from the initial state, and iterate day by day until we reach the final state:
16 2 Optimal stock market strategy

def max_profit(daily_price):
# Initial state: start from a reference cash amount.
# It can be any value.
# We use 0 and allow our cash to go below 0 if we need to buy a share.
cash_not_owning_share = 0
# High penalty for owning a stock initially:
# ensures this option is never chosen.
cash_owning_share = ­float('inf')
for price in daily_price:
# Transitions to the current day, owning the stock:
strategy_buy = cash_not_owning_share ­ price
strategy_hold = cash_owning_share
# Transitions to the current day, not owning the stock:
strategy_sell = cash_owning_share + price
strategy_avoid = cash_not_owning_share
# Compute the new states.
cash_owning_share = max(strategy_buy, strategy_hold)
cash_not_owning_share = max(strategy_sell, strategy_avoid)
# The profit is the final cash amount, since we start from
# a reference of 0.
return cash_not_owning_share

At each step, we only need to store the profit corresponding to the two states of that day. This
is due to the state machine not having any transitions between non-consecutive days: we
could say that at any time, the state machine does not “remember” anything from the days
before yesterday.

The time complexity is 𝑂(𝑛), but the space complexity has been reduced to 𝑂(1), since we
only need to store the result for the previous day.

Bottom-up solutions often have smaller space complexity than top-down


It is very common (with some exceptions) that bottom-up solutions have lower memory
requirements than top-down. This is due to the ability to control precisely what data we
store and what data we discard: instead of a LRU cache policy, we can keep only the data
we need. In addition, they do not suffer from the overhead of storing stack frames due
to recursion, which is a hidden cost of the top-down solutions.

Variation: limited investment budget


In a variation of the problem, the investment budget is limited: we start with a fixed amount
of money, and we are not allowed to buy a share if we cannot afford it (we cannot borrow
money).

We can adjust the solution for this constraint:


17

def max_profit(daily_price, budget):


# Initial state.
cash_not_owning_share = budget
# High penalty for owning a stock initially:
# ensures this option is never chosen.
cash_owning_share = ­float('inf')
for price in daily_price:
# Transitions to the current day, owning the stock:
strategy_buy = cash_not_owning_share ­ price
strategy_hold = cash_owning_share
# Transitions to the current day, not owning the stock:
strategy_sell = cash_owning_share + price
strategy_avoid = cash_not_owning_share
# Compute the new states.
cash_owning_share = max(strategy_buy, strategy_hold)
if cash_owning_share < 0:
# We cannot afford to buy the share at this time.
# Add a high penalty to ensure we never choose this option.
cash_owning_share = ­float('inf')
cash_not_owning_share = max(strategy_sell, strategy_avoid)
return cash_not_owning_share ­ budget

Any time the optimal cash amount in a given state goes below zero, we replace it with negative
infinity. This ensures that this path through the state machine will not be chosen. We only
have to do this for the states where we own stock. In the states where we do not own stock,
our cash amount never decreases from the previous day, so this check is not needed.

Expect follow-up questions


One of the most common mistakes candidates make is to assume that they have to solve
a single problem during a time slot of the interview. Taking too long to solve it does
not leave any time for follow-up questions, which puts the candidate at a disadvantage
compared to others. An old Italian proverb applies here: perfect is the enemy of good. Do
not get lost in too many details trying to make your solution perfect; reach an agreement
with your interviewer on when it is good enough, so that you have time to take 1-2
follow-up questions such as this one.

Variation: limited number of transactions


In anover variation of the problem, the total number of transactions that can be performed
is bounded: the stock can only be sold up to a certain number of times tx_limit.
In this variation, the state machine needs to be adjusted so that it keeps track of multiple
pieces of information:
• whether we own the stock or not at the end of the day;
18 2 Optimal stock market strategy

• how many times we have sold the stock so far.


Instead of having only 2 states each day (for owning or not owning the stock), we now have
up to 2 * tx_limit depending on how many times we have sold, per day. For each of these
states, we have to compute the best amount of money we can earn.
The transitions between the states need to take into account the operation (buying, holding,
selling, avoiding) and whether it leads to the next day state with the same transaction count
or one higher.
We can write the following implementation:
def max_profit(daily_price, tx_limit):
# cash_not_owning_share[k] = amount of cash at the end of the day,
# if we do not own the share, and we have sold k times so far.
# Initially we have sold 0 times and we start from a reference
# budget of 0. Any other state is invalid.
cash_not_owning_share = [­float('inf')] * (tx_limit + 1)
cash_not_owning_share[0] = 0
# cash_owning_share[k] = amount of cash at the end of the day,
# if we own the share, and we have sold k times so far.
# Initially we do not own any stock, so set the state to invalid.
cash_owning_share = [­float('inf')] * (tx_limit + 1)
for price in daily_price:
# Initialize the next day's states with ­Infinity,
# then update them with the best possible transition.
cash_not_owning_share_next = [­float('inf')] * (tx_limit + 1)
cash_owning_share_next = [­float('inf')] * (tx_limit + 1)
for prev_tx_count in range(tx_limit):
# Transition to the current day, owning the stock:
strategy_buy = cash_not_owning_share[prev_tx_count] ­ price
strategy_hold = cash_owning_share[prev_tx_count]
# Transitions to the current day, not owning the stock:
strategy_sell = cash_owning_share[prev_tx_count] + price
strategy_avoid = cash_not_owning_share[prev_tx_count]
# Compute the new states.
if prev_tx_count < tx_limit:
# Selling increases the tx_count by 1.
cash_not_owning_share_next[prev_tx_count + 1] = max(
cash_not_owning_share_next[prev_tx_count + 1],
strategy_sell)
# All other transitions keep tx_count the same.
cash_not_owning_share_next[prev_tx_count] = max(
cash_not_owning_share_next[prev_tx_count],
strategy_avoid)
cash_owning_share_next[prev_tx_count] = max(
cash_owning_share_next[prev_tx_count],
19

strategy_buy,
strategy_hold)
cash_not_owning_share = cash_not_owning_share_next
cash_owning_share = cash_owning_share_next
# We have multiple final states, depending on how many times we sold.
# The transaction limit may not have been reached.
# Choose the most profitable final state.
return max(cash_not_owning_share)

Master state machines


While you are reading this book, take your time to understand well how to model prob-
lems using state machines. Almost all dynamic programming problems can be solved
this way. This skill is useful not just for interviews, but also in general in your software
engineer career.
20 3 Change-making

3 Change-making

Given a money amount and a list of coin denominations, provide the combination of coins
adding up to that amount, that uses the fewest coins.

Example 1: Pay amount 9 using coin denominations [1, 2, 5]. The combination having
the fewest coins is [5, 2, 2]. A suboptimal combination is [5, 1, 1, 1, 1]: it adds up
to 9, but is using 5 coins instead of 3, thus it cannot be the solution.

Example 2: Pay amount 12 using coin denominations [1, 6, 10]. The combination having
the fewest coins is [6, 6]. A suboptimal combination is [10, 1, 1].

Clarification questions

Q: What result should be returned for total amount 0?


A: The empty list [].

Q: Is it possible that the amount cannot be paid with the given coins?
A: Yes. For example, 5 cannot be paid with coins [2, 4]. In such a situation, return None.

Solution 1: dynamic programming, top-down, 𝑂(𝑛𝑣) time

We can formulate a top-down dynamic programming solution if we model the problem as


a recurrence. For any non-zero amount that has to be paid optimally using the given coins,
we know that at least one of the coins has to be used. The problem is that we do not know
which one. If we knew, we could use that as a starting point to reach a subproblem: we could
subtract its value from the amount, then solve an instance of the problem for the remaining,
smaller amount. We would continue in this way until the remaining amount becomes 0.

However, we do not know which coin to choose first optimally. In this situation, we have
no other choice but try all possible options in brute-force style. For each coin, we subtract
its value from the amount, then solve by recurrence the subproblem—this leads to a candi-
date solution for each choice of the first coin. Once we are done, we compare the candidate
solutions and choose the one using the fewest coins.

Here is an example of how we would form the optimal change for amount 9, using coins [1,
2, 5]. We represent each amount as a node in a tree. Whenever we subtract a coin value
from that amount, we add an edge to a new node with a smaller value. Please mind that the
actual solution does not use trees, at least not explicitly: they are shown here only for clarity.
21

This diagram shows that for value 9, we have three options for choosing the first coin:

• We choose coin 1. We now have to solve the subproblem for value 9 − 1 = 8. Suppose
its optimal result is [5, 2, 1]. Then we add coin 1 to create the candidate solution
[5, 2, 1, 1] for 9.
• We choose coin 2. We now have to solve the subproblem for value 9 − 2 = 7. Suppose
the optimal result is [5, 2]. We add to it coin 2 to create the candidate solution [5,
2, 2] for 9.
• We choose coin 5. We now have to solve the subproblem for value 9 − 5 = 4. The
optimal result is [2, 2]. We add to it coin 5 to create the candidate solution [5, 2,
2] for 9.

Now that we are done solving the subproblems, we compare the candidate solutions and
choose the one using the fewest coins: [5, 2, 2].

For solving the subproblems, we use the same procedure. The only difference is that we
need to pay attention to two edge cases: the terminating condition (reaching amount 0), and
avoiding choices where we are paying too much (reaching negative amounts). To understand
these better, let’s take a look at the subproblems:
22 3 Change-making

Drawing the subproblems helps us see clearly the edge cases:


• When the amount becomes 0, we have to stop the iteration, since the subproblem is
solved and there is nothing left to be paid. We can see the 0 node in several of the
subproblem trees.
• When the amount goes below zero, the given amount cannot be formed with that
coin. For example, trying to pay amount 3 using coin 5 would require solving the
subproblem for amount -2, which does not make sense.
In addition to that, we can also notice that there are many redundancies among the sub-
problems. This is very important, as it affects the performance of the algorithm. We origi-
nally thought about solving the problem using brute force, which results in a slow algorithm:
𝑂(𝑛𝑣 ), where 𝑛 is the number of coins and 𝑣 is the value of the amount we have to pay. In other
words, the brute-force solution has exponential complexity, with very poor performance.
However, now that we know that there are redundancies among the subproblems, we can
cache their results to reduce the complexity to only 𝑂(𝑛𝑣): in each step we must try 𝑛 coins,
and we have up to 𝑣 steps (in the worst case, we pay 𝑣 with 𝑣 coins of value 1). This result is
great: we reduced the time complexity from exponential to polynomial!
We are now ready to write a first implementation of the recurrence, without caching (the
brute-force, exponential complexity solution). This is because we want to focus on the logic
of the recurrence, without distractions; we will add caching afterwards.
def make_change(coins, amount):
"""
Given a list of coin values, and an amount to be paid,
returns the shortest list of coins that add up to that amount.
If the amount to be paid is zero, the empty list is returned.
23

If the amount cannot be paid using the coins, None is returned.


"""
# Handle payment of amount zero.
if not amount:
return []
# Negative amounts cannot be paid.
if amount < 0:
return None
optimal_result = None
# Consider all the possible ways to choose the last coin.
for coin in coins:
# Solve a subproblem for the rest of the amount.
partial_result = make_change(coins, amount ­ coin)
# Skip this coin if the payment failed:
if partial_result is None:
continue
candidate = partial_result + [coin]
# Check if the candidate solution is better than the
# optimal solution known so far, and update it if needed.
if (optimal_result is None or
len(candidate) < len(optimal_result)):
optimal_result = candidate
return optimal_result

This algorithm implements the recurrence relation as explained above. Notice how we handle
the edge cases at the beginning of the function, before making any recursive calls, to avoid
infinite recursion and to keep the recursion logic as simple as possible.

Handle and eliminate edge cases early


Generally, it is preferrable to get edge cases out of the way as soon as possible, so that the
rest of the implementation is kept simple. This improves substantially the readability of
the code.

This implementation has exponential time complexity, since we have not implemented
caching yet. Let’s do that now.
Unfortunately, if we try to add caching simply adding the lru_cache decorator, we will have
a nasty surprise:
from functools import lru_cache

@lru_cache(maxsize=None)
def make_change(coins, amount):
...

This code throws the exception: TypeError: unhashable type: 'list'. This is caused by
24 3 Change-making

the inability to cache the argument coins. As a list, it is mutable, and the lru_cache deco-
rator rejects it. The decorator supports caching only arguments with immutable types, such
as numbers, strings or tuples. This is for a very good reason: to prevent bugs in case mutable
arguments are changed later (in case of lists, via append, del or changing its elements), which
would require invalidating the cache.

A joke circulating in software engineering circles says that there are only two hard problems
in computer science: cache invalidation, naming things, and off-by-one errors. To address
the former, the design of the lru_cache decorator takes the easy way out: it avoids having to
implement cache invalidation at all by only allowing immutable arguments.

Still, we need to add caching one way or another. We can work around the lru_cache limi-
tation if we notice that we do not actually need to cache the coins list—that is shared among
all subproblems. We only need to pass the remaining amount to be paid. So we write a helper
function that solves the subproblem, taking as argument only the amount. The coin list is
shared between invocations.

One way to implement this is to make the helper function nested inside the make_change
function, so that it has access to the coins argument of the outer function:
def make_change(coins, amount):
@lru_cache(maxsize=None)
def helper(amount):
...
return helper(amount)

Another way is to transform the make_change function into a method of a class, that stores
the list of coins in a class member. The helper could then be written as another method of
the class, ideally a private one. I think adding classes is overkill for what we have to do, so we
will not discuss this approach.

Here is the full solution that uses nested functions:


def make_change(coins, amount):
"""
Given a list of coin values, and an amount to be paid,
returns the shortest list of coins that add up to that amount.
If the amount to be paid is zero, the empty list is returned.
If the amount cannot be paid using the coins, None is returned.
"""
@lru_cache(maxsize=None)
def helper(amount):
# Handle payment of amount zero.
if not amount:
return []
# Negative amounts cannot be paid.
if amount < 0:
return None
25

optimal_result = None
# Consider all the possible ways to choose the last coin.
for coin in coins:
# Solve a subproblem for the rest of the amount.
partial_result = helper(amount ­ coin)
# Skip this coin if the payment failed:
if partial_result is None:
continue
candidate = partial_result + [coin]
# Check if the candidate solution is better than the
# optimal solution known so far, and update it if needed.
if (optimal_result is None or
len(candidate) < len(optimal_result)):
optimal_result = candidate
return optimal_result
return helper(amount)

How many comments should we write?


Well-written comments improve code readability, however there is a trade-off: too many
comments can be distracting, are a maintainability burden, and a sign that the code
might not be clear enough. However, for interviews, I prefer leaning towards more ver-
bosity, since we normally do not have time to refactor the code to perfection. Comments
can compensate for that.
A good rule of thumb is to use comments to explain why the code is doing something.
If you feel the need to explain what it is doing, it might be time to refactor that piece of
code into an appropriately-named function. If you do not have time to refactor during
the interview, you can add a comment like: “TODO: refactor into helper function.”

Solution 2: dynamic programming, bottom-up, 𝑂(𝑛𝑣) time


Once we have implemented the top-down solution, we can rewrite it as bottom-up: we start
from the amount 0, and keep adding coins in all possible ways until reaching the amount to
be paid:
def make_change(coins, amount):
# solutions[k] = optimal list of coins that add up to k,
# or None if no solution is known for k
solutions = [None] * (amount + 1)
# Initial state: no coins needed to pay amount 0
solutions[0] = []
# Starting from amount 0, find ways to pay higher amounts
# by adding coins.
paid = 0
while paid < amount:
26 3 Change-making

if solutions[paid] is not None:


for coin in coins:
next_paid = paid + coin
if next_paid > amount:
continue
if (solutions[next_paid] is None or
len(solutions[next_paid]) >
len(solutions[paid]) + 1):
solutions[next_paid] = solutions[paid] + [coin]
paid += 1
return solutions[amount]

This solution is iterative, which is an advantage compared to the top-down solution, since it
avoids the overheads of recursive calls. However, for certain denominations, it wastes time
by increasing the amount very slowly, in steps of 1 unit. For example, if coins = [100,
200, 500] (suppose we use bills), it does not make sense to advance in steps of 1.

In addition, a lot of space is wasted for amounts that cannot be paid. Let’s see if we can come
up with a better solution.

Solution 3: dynamic programming + BFS, bottom-up, 𝑂(𝑛𝑣) time


We can optimize the bottom-up solution by using a queue of amounts to be handled, instead
of an array. This helps in two ways: Firstly, it allows us to skip amounts that cannot be formed,
thus reducing execution time. Secondly, by not having to store amounts that cannot be paid,
memory usage is reduced.
Let’s implement it:
def simplest_change(coins, amount):
# solutions[k] = optimal list of coins that add up to amount k
# Amounts that cannot be paid are not stored.
solutions = {0: []}
# List of amounts that can be paid but have not been handled yet.
amounts_to_be_handled = collections.deque([0])
# Iterate over amounts in breadth­first order.
while amounts_to_be_handled:
paid = amounts_to_be_handled.popleft()
solution = solutions[paid]
if paid == amount:
# Due to BFS order, the first path reaching the
# required amount is the one using the smallest number
# of coins. Thus it is the optimal solution.
return solution
for coin in coins:
next_paid = paid + coin
if next_paid > amount:
27

# We can safely ignore amounts overshooting the


# target amount to be paid.
continue
if next_paid not in solutions:
solutions[next_paid] = solution + [coin]
amounts_to_be_handled.append(next_paid)
# No combination of coins could match the required amount,
# thus it cannot be paid.
return None

Notice how we are treating the (sub)problem space as a graph, which is explored in breadth-
first order (BFS). We start from the subproblem of forming the amount 0. From there, we
keep expanding using all the possible coin choices until we reach the required amount.

Let’s look at an example showing how the problem space exploration works for paying
amount 9 with coins [1, 2, 5]. We start with amount 0 and explore by adding a coin in all
possible ways. Here is the first level of the problem space graph:

The first level contains all amounts that can be paid using a single coin. After this step, we
have [1, 2, 5] in our BFS queue, which is exactly the list of nodes on the first level—hence
the name of breadth-first search.

To fill in the second level of the problem space graph, we continue the exploration a step
further for amounts 1, 2 and 5:
28 3 Change-making

The second level contains all possible amounts that can be paid with 2 coins. After this step,
we have [3, 6, 4, 7] in our BFS queue.

Notice that we discarded nodes corresponding to already known amounts, since these can
be paid with a similar or better solution (fewer coins). We have also discarded amounts that
exceed the value we have to pay (such as 10). This ensures that the graph size is bounded and
that it contains only valid/optimal nodes.

We still have not found a way to pay our target amount, 9. Let’s explore further, adding the
third level of the problem space graph:

Note that as we reached the target amount 9, we stopped drawing the rest of the level. As
it is on the third level of the tree, the amount can be paid with 3 coins. The combination is
29

2, 2 and 5, which can be found by walking the path from 0 to 9. This is the solution to the
problem.
Note that the exploration order was important: the breadth-first order guarantees that each
time we reach a new amount, the path we followed is the shortest possible in terms of num-
ber of coins used. Had we used instead another graph search algorithm, such as depth-first
search, the solution might not have been optimal.

Variant: count the number of ways to pay (permutations)


Given a money amount and a list of coin denominations, count in how many ways it is pos-
sible to pay that amount. The order matters, i.e. paying with a coin of 1 followed by a coin of
2 is considered distinct from paying with a coin of 2 followed by a coin of 1.
Example: To pay amount 3 with coins [1, 2], there are 3 possible ways: 1 + 2, 2 + 1 and
1 + 1 + 1.

Solution: dynamic-programming, top-down, 𝑂(𝑛𝑣)


Let’s analyze a slightly more complicated example: paying amount 6 with coins [1, 2, 5].
Here are all the possible ways to pay the amount:
• 6 = 5 + 1
• 6 = 1 + 5
• 6 = 2 + 2 + 2
• 6 = 2 + 2 + 1 + 1
• 6 = 2 + 1 + 2 + 1
• 6 = 2 + 1 + 1 + 2
• 6 = 1 + 2 + 2 + 1
• 6 = 1 + 2 + 1 + 2
• 6 = 1 + 1 + 2 + 2
• 6 = 2 + 1 + 1 + 1 + 1
• 6 = 1 + 2 + 1 + 1 + 1
• 6 = 1 + 1 + 2 + 1 + 1
• 6 = 1 + 1 + 1 + 2 + 1
• 6 = 1 + 1 + 1 + 1 + 2
• 6 = 1 + 1 + 1 + 1 + 1 + 1

From this list we do not see any particular rule, other than enumerating all the permutations
of the combinations of coins that add up to 6 (1 + 5, 2 + 2 + 2, 2 + 2 + 1 + 1, 2 + 1 +
1 + 1 + 1 and 1 + 1 + 1 + 1 + 1 +1). This is not very helpful.

Let’s list the payments again but sorted in lexicographic order:


• 6 = 1 + 1 + 1 + 1 + 1 + 1
• 6 = 1 + 1 + 1 + 1 + 2
• 6 = 1 + 1 + 1 + 2 + 1
• 6 = 1 + 1 + 2 + 1 + 1
• 6 = 1 + 1 + 2 + 2
Another Random Scribd Document
with Unrelated Content
By ANTHONY TROLLOPE.
The Way we Live Now.
Frau Frohmann.
Marion Fay.
Scarborough’s Family.
The Land Leaguers.

By IVAN TURGENIEFF, &c.—Stories from Foreign Novelists.


By MARK TWAIN.
Mark Twain’s Choice Works.
Mark Twain’s Library of Humour.
The Innocents Abroad.
Roughing It; and The Innocents at Home.
A Tramp Abroad.
The American Claimant.
Adventures of Tom Sawyer.
Tom Sawyer Abroad.
Tom Sawyer, Detective.
Pudd’nhead Wilson.
The Gilded Age.
Prince and the Pauper.
Life on the Mississippi.
The Adventures of Huckleberry Finn.
A Yankee at the Court of King Arthur.
Stolen White Elephant.
£1,000,000 Bank-note.

By C. C. FRASER-TYTLER.—Mistress Judith.
By SARAH TYTLER.
Buried Diamonds.
The Blackhall Ghosts.
The Macdonald Lass.
The Witch-Wife.
Mrs. Carmichael’s Goddesses.
Lady Bell.
Rachel Langton.
Sapphira.
A Honeymoon’s Eclipse.

By ALLEN UPWARD.
The Queen against Owen.
The Prince of Balkistan.

By E. A. VIZETELLY.—The Scorpion: A Romance of Spain.


By F. WARDEN.—Joan, the Curate.
By CY WARMAN.—The Express Messenger.
By WILLIAM WESTALL.
For Honour and Life.
A Woman Tempted Him.
Her Two Millions.
Two Pinches of Snuff.
Roy of Roy’s Court.
Nigel Fortescue.
Birch Dene.
The Phantom City.
A Queer Race.
Ben Clough.
The Old Factory.
Red Ryvington.
Ralph Norbreck’s Trust.
Trust-money.
Sons of Belial.
Strange Crimes.

By ATHA WESTBURY.—The Shadow of Hilton Fernbrook.


By C. J. WILLS.—An Easy-going Fellow.
By JOHN STRANGE WINTER.
Cavalry Life and Regimental Legends.
A Soldier’s Children.

By MARGARET WYNMAN.—My Flirtations.


By E. ZOLA.
The Fortune of the Rougons.
The Abbe Mouret’s Transgression.
The Downfall.
The Dream.
Money.
Dr. Pascal.
Lourdes.
The Fat and the Thin.
His Excellency.
The Dram-Shop.
Rome.
Paris.
Fruitfulness.

By ‘Z. Z.’—A Nineteenth Century Miracle.


CHEAP EDITIONS OF POPULAR NOVELS
Post 8vo, illustrated boards, 2s. each.
By ARTEMUS WARD.
Artemus Ward Complete.

By EDMOND ABOUT.
The Fellah.

By HAMILTON AÏDÉ.
Carr of Carrlyon.
Confidences.

By Mrs. ALEXANDER.
Maid, Wife, or Widow?
Blind Fate.
Valerie’s Fate.
A Life Interest.
Mona’s Choice.
By Woman’s Wit.

By GRANT ALLEN.
Philistia.
Babylon.
Strange Stories.
For Mamie’s Sake.
In all Shades.
The Beckoning Hand.
The Devil’s Die.
The Tents of Shem.
The Great Taboo.
Dumaresq’s Daughter.
Duchess of Powysland.
Blood Royal.
Ivan Greet’s Masterpiece.
The Scallywag.
This Mortal Coil.
At Market Value.
Under Sealed Orders.

By E. LESTER ARNOLD.
Phra the Phœnician.

BY FRANK BARRETT.
Fettered for Life.
Little Lady Linton.
Between Life & Death.
Sin of Olga Zassoulich.
Folly Morrison.
Lieut. Barnabas.
Honest Davie.
A Prodigal’s Progress.
Found Guilty.
A Recoiling Vengeance.
For Love and Honour.
John Ford, &c.
Woman of Iron Brace’ts.
The Harding Scandal.
A Missing Witness.

By SHELSLEY BEAUCHAMP.
Grantley Grange.

By FREDERICK BOYLE.
Camp Notes.
Savage Life.
Chronicles of No-man’s Land.

By Sir W. BESANT and J. RICE.


Ready-Money Mortiboy.
My Little Girl.
With Harp and Crown.
This Son of Vulcan.
The Golden Butterfly.
The Monks of Thelema.
By Celia’s Arbour.
Chaplain of the Fleet.
The Seamy Side.
The Case of Mr. Lucraft.
In Trafalgar’s Bay.
The Ten Years’ Tenant.

By Sir WALTER BESANT.


All Sorts and Conditions of Men.
The Captains’ Room.
All in a Garden Fair.
Dorothy Forster.
Uncle Jack.
The World Went Very Well Then.
Children of Gibeon.
Herr Paulus.
For Faith and Freedom.
To Call Her Mine.
The Master Craftsman.
The Bell of St. Paul’s.
The Holy Rose.
Armorel of Lyonesse.
S. Katherine’s by Tower.
Verbena Camellia Stephanotis.
The Ivory Gate.
The Rebel Queen.
Beyond the Dreams of Avarice.
The Revolt of Man.
In Deacon’s Orders.
The City of Refuge.

By AMBROSE BIERCE.
In the Midst of Life.

BY BRET HARTE.
Californian Stories.
Gabriel Conroy.
Luck of Roaring Camp.
An Heiress of Red Dog.
Flip.
Maruja.
A Phyllis of the Sierras.
A Waif of the Plains.
Ward of Golden Gate.

By ROBERT BUCHANAN.
Shadow of the Sword.
A Child of Nature.
God and the Man.
Love Me for Ever.
Foxglove Manor.
The Master of the Mine.
Annan Water.
The Martyrdom of Madeline.
The New Abelard.
The Heir of Linne.
Woman and the Man.
Rachel Dene.
Matt.
Lady Kilpatrick.

By BUCHANAN and MURRAY.


The Charlatan.

By HALL CAINE.
The Shadow of a Crime.
A Son of Hagar.
The Deemster.

By Commander CAMERON.
The Cruise of the ‘Black Prince.’

By HAYDEN CARRUTH.
The Adventures of Jones.
By AUSTIN CLARE.
For the Love of a Lass.

By Mrs. ARCHER CLIVE.


Paul Ferroll.
Why Paul Ferroll Killed his Wife.

By MACLAREN COBBAN.
The Cure of Souls.
The Red Sultan.

By C. ALLSTON COLLINS.
The Bar Sinister.

By MORT. & FRANCES COLLINS.


Sweet Anna Page.
Transmigration.
From Midnight to Midnight.
A Fight with Fortune.
Sweet and Twenty.
The Village Comedy.
You Play me False.
Blacksmith and Scholar.
Frances.

By WILKIE COLLINS.
Armadale.
After Dark.
No Name.
Antonina.
Basil.
Hide and Seek.
The Dead Secret.
Queen of Hearts.
Miss or Mrs.?
The New Magdalen.
The Frozen Deep.
The Law and the Lady.
The Two Destinies.
The Haunted Hotel.
A Rogue’s Life.
My Miscellanies.
The Woman in White.
The Moonstone.
Man and Wife.
Poor Miss Finch.
The Fallen Leaves.
Jezebel’s Daughter.
The Black Robe.
Heart and Science.
‘I Say No!’
The Evil Genius.
Little Novels.
Legacy of Cain.
Blind Love.

By M. J. COLQUHOUN.
Every Inch a Soldier.

By C. EGBERT CRADDOCK.
The Prophet of the Great Smoky Mountains.
By MATT CRIM.
The Adventures of a Fair Rebel.

By B. M. CROKER.
Pretty Miss Neville.
Diana Barrington.
‘To Let.’
A Bird of Passage.
Proper Pride.
A Family Likeness.
A Third Person.
Village Tales and Jungle Tragedies.
Two Masters.
Mr. Jervis.
The Real Lady Hilda.
Married or Single?
Interference.

By W. CYPLES.
Hearts of Gold.

By ALPHONSE DAUDET.
The Evangelist; or, Port Salvation.

By ERASMUS DAWSON.
The Fountain of Youth.

By JAMES DE MILLE.
A Castle in Spain.

By J. LEITH DERWENT.
Our Lady of Tears.
Circe’s Lovers.

By DICK DONOVAN.
The Man-Hunter.
Tracked and Taken.
Caught at Last!
Wanted!
Who Poisoned Hetty Duncan?
Man from Manchester.
A Detective’s Triumphs.
In the Grip of the Law.
From Information Received.
Tracked to Doom.
Link by Link.
Suspicion Aroused.
Dark Deeds.
Riddles Read.
The Mystery of Jamaica Terrace.
The Chronicles of Michael Danevitch.

By Mrs. ANNIE EDWARDES.


A Point of Honour.
Archie Lovell.

By M. BETHAM-EDWARDS.
Felicia.
Kitty.

By EDWARD EGGLESTON.
Roxy.
By G. MANVILLE FENN.
The New Mistress.
Witness to the Deed.
The Tiger Lily.
The White Virgin.

By PERCY FITZGERALD.
Bella Donna.
Never Forgotten.
Polly.
Fatal Zero.
Second Mrs. Tillotson.
Seventy-five Brooke Street.
The Lady of Brantome.

By P. FITZGERALD and others.


Strange Secrets.

By ALBANY DE FONBLANQUE.
Filthy Lucre.

By R. E. FRANCILLON.
Olympia.
One by One.
A Real Queen.
Queen Cophetua.
King or Knave?
Romances of the Law.
Ropes of Sand.
A Dog and his Shadow.
By HAROLD FREDERIC.
Seth’s Brother’s Wife.
The Lawton Girl.

Prefaced by Sir BARTLE FRERE.


Pandurang Hari.

By EDWARD GARRETT.
The Capel Girls.

By GILBERT GAUL.
A Strange Manuscript.

By CHARLES GIBBON.
Robin Gray.
Fancy Free.
For Lack of Gold.
What will the World Say?
In Love and War.
For the King.
In Pastures Green.
Queen of the Meadow.
A Heart’s Problem.
The Dead Heart.
In Honour Bound.
Flower of the Forest.
The Braes of Yarrow.
The Golden Shaft.
Of High Degree.
By Mead and Stream.
Loving a Dream.
A Hard Knot.
Heart’s Delight.
Blood-Money.

By WILLIAM GILBERT.
Dr. Austin’s Guests.
James Duke.
The Wizard of the Mountain.

By ERNEST GLANVILLE.
The Lost Heiress.
A Fair Colonist.
The Fossicker.

By Rev. S. BARING GOULD.


Red Spider.
Eve.

By HENRY GREVILLE.
A Noble Woman.
Nikanor.

By CECIL GRIFFITH.
Corinthia Marazion.

By SYDNEY GRUNDY.
The Days of his Vanity.

By JOHN HABBERTON.
Brueton’s Bayou.
Country Luck.
By ANDREW HALLIDAY.
Every-day Papers.

By THOMAS HARDY.
Under the Greenwood Tree.

By JULIAN HAWTHORNE.
Garth.
Ellice Quentin.
Fortune’s Fool.
Miss Cadogna.
Sebastian Strome.
Dust.
Beatrix Randolph.
Love—or a Name.
David Poindexter’s Disappearance.
The Spectre of the Camera.

By Sir ARTHUR HELPS.


Ivan de Birou.

By G. A. HENTY.
Rujub the Juggler.

By HENRY HERMAN.
A Leading Lady.

By HEADON HILL.
Zambra the Detective.

By JOHN HILL.
Treason Felony.
By Mrs. CASHEL HOEY.
The Lover’s Creed.

By Mrs. GEORGE HOOPER.


The House of Raby.

By Mrs. HUNGERFORD.
A Maiden all Forlorn.
In Durance Vile.
Marvel.
A Mental Struggle.
A Modern Circe.
April’s Lady.
Peter’s Wife.
Lady Verner’s Flight.
The Red House Mystery.
The Three Graces.
Unsatisfactory Lover.
Lady Patty.
Nora Creina.
Professor’s Experiment.

By Mrs. ALFRED HUNT.


Thornicroft’s Model.
That Other Person.
Self-Condemned.
The Leaden Casket.

By WM. JAMESON.
My Dead Self.
By HARRIETT JAY.
The Dark Colleen.
Queen of Connaught.

By MARK KERSHAW.
Colonial Facts and Fictions.

By R. ASHE KING.
A Drawn Game.
‘The Wearing of the Green.’
Passion’s Slave.
Bell Barry.

By EDMOND LEPELLETIER.
Madame Sans Gene.

By JOHN LEYS.
The Lindsays.

By E. LYNN LINTON.
Patricia Kemball.
The World Well Lost.
Under which Lord?
Paston Carew.
‘My Love!’
Ione.
With a Silken Thread.
The Atonement of Leam Dundas.
Rebel of the Family.
Sowing the Wind.
The One Too Many.
Dulcie Everton.

By HENRY W. LUCY.
Gideon Fleyce.

By JUSTIN McCARTHY.
Dear Lady Disdain.
Waterdale Neighbours.
My Enemy’s Daughter.
A Fair Saxon.
Linley Rochford.
Miss Misanthrope.
Camiola.
Donna Quixote.
Maid of Athens.
The Comet of a Season.
The Dictator.
Red Diamonds.
The Riddle Ring.

By HUGH MACCOLL.
Mr. Stranger’s Sealed Packet.

By GEORGE MACDONALD.
Heather and Snow.

By AGNES MACDONELL.
Quaker Cousins.

By KATHARINE S. MACQUOID.
The Evil Eye.
Lost Rose.

By W. H. MALLOCK.
A Romance of the Nineteenth Century.
The New Republic.

By J. MASTERMAN.
Half-a-dozen Daughters.

By BRANDER MATTHEWS.
A Secret of the Sea.

By L. T. MEADE.
A Soldier of Fortune.

By LEONARD MERRICK.
The Man who was Good.

By JEAN MIDDLEMASS.
Touch and Go.
Mr. Dorillion.

By Mrs. MOLESWORTH.
Hathercourt Rectory.

By J. E. MUDDOCK.
Stories Weird and Wonderful.
The Dead Man’s Secret.
From the Bosom of the Deep.

By D. CHRISTIE MURRAY.
A Model Father.
Joseph’s Coat.
Coals of Fire.
Val Strange.
Hearts.
Old Blazer’s Hero.
The Way of the World.
Cynic Fortune.
A Life’s Atonement.
By the Gate of the Sea.
A Bit of Human Nature.
First Person Singular.
Bob Martin’s Little Girl.
Time’s Revenges.
A Wasted Crime.
In Direst Peril.
Mount Despair.
A Capful o’ Nails.

By MURRAY and HERMAN.


One Traveller Returns.
Paul Jones’s Alias.
The Bishops’ Bible.

By HENRY MURRAY.
A Game of Bluff.
A Song of Sixpence.

By HUME NISBET.
‘Bail Up!’
Dr. Bernard St. Vincent.
By W. E. NORRIS.
Saint Ann’s.
Billy Bellew.

By ALICE O’HANLON.
The Unforeseen.
Chance? or Fate?

By GEORGES OHNET.
Dr. Rameau.
A Last Love.
A Weird Gift.

By Mrs. OLIPHANT.
Whiteladies.
The Primrose Path.
The Greatest Heiress in England.

By Mrs. ROBERT O’REILLY.


Phœbe’s Fortunes.

By OUIDA.
Held in Bondage.
Strathmore.
Chandos.
Idalia.
Under Two Flags.
Cecil Castlemaine’s Gage.
Tricotrin.
Puck.
Folle Farine.
A Dog of Flanders.
Pascarel.
Signa.
Princess Napraxine.
In a Winter City.
Ariadne.
Friendship.
Two Lit. Wooden Shoes.
Moths.
Bimbi.
Pipistrello.
A Village Commune.
Wanda.
Othmar.
Frescoes.
In Maremma.
Guilderoy.
Ruffino.
Syrlin.
Santa Barbara.
Two Offenders.
Ouida’s Wisdom, Wit, and Pathos.

By MARGARET AGNES PAUL.


Gentle and Simple.

By EDGAR A. POE.
The Mystery of Marie Roget.

By Mrs. CAMPBELL PRAED.


The Romance of a Station.
The Soul of Countess Adrian.
Outlaw and Lawmaker.
Christina Chard.
Mrs. Tregaskiss.

By E. C. PRICE.
Valentina.
The Foreigners.
Mrs. Lancaster’s Rival.
Gerald.

By RICHARD PRYCE.
Miss Maxwell’s Affections.

By JAMES PAYN.
Bentinck’s Tutor.
Murphy’s Master.
A County Family.
At Her Mercy.
Cecil’s Tryst.
The Clyffards of Clyffe.
The Foster Brothers.
Found Dead.
The Best of Husbands.
Walter’s Word.
Halves.
Fallen Fortunes.
Humorous Stories.
£200 Reward.
A Marine Residence.
Mirk Abbey.
By Proxy.
Under One Roof.
High Spirits.
Carlyon’s Year.
From Exile.
For Cash Only.
Kit.
The Canon’s Ward.
The Talk of the Town.
Holiday Tasks.
A Perfect Treasure.
What He Cost Her.
A Confidential Agent.
Glow-worm Tales.
The Burnt Million.
Sunny Stories.
Lost Sir Massingberd.
A Woman’s Vengeance.
The Family Scapegrace.
Gwendoline’s Harvest.
Like Father, Like Son.
Married Beneath Him.
Not Wooed, but Won.
Less Black than We’re Painted.
Some Private Views.
A Grape from a Thorn.
The Mystery of Mirbridge.
The Word and the Will.
A Prince of the Blood.
A Trying Patient.

By CHARLES READE.
It is Never Too Late to Mend.
Christie Johnstone.
The Double Marriage.
Put Yourself in His Place.
Love Me Little, Love Me Long.
The Cloister and the Hearth.
The Course of True Love.
The Jilt.
The Autobiography of a Thief.
A Terrible Temptation.
Foul Play.
The Wandering Heir.
Hard Cash.
Singleheart and Doubleface.
Good Stories of Man and other Animals.
Peg Woffington.
Griffith Gaunt.
A Perilous Secret.
A Simpleton.
Readiana.
A Woman-Hater.

By Mrs. J. H. RIDDELL.
Weird Stories.
Fairy Water.
Her Mother’s Darling.
The Prince of Wales’s Garden Party.
The Uninhabited House.
The Mystery in Palace Gardens.
The Nun’s Curse.
Idle Tales.

By AMELIE RIVES.
Barbara Dering.

By F. W. ROBINSON.
Women are Strange.
The Hands of Justice.
The Woman in the Dark.

By JAMES RUNCIMAN.
Skippers and Shellbacks.
Grace Balmaign’s Sweetheart.
Schools and Scholars.

By W. CLARK RUSSELL.
Round the Galley Fire.
On the Fo’k’sle Head.
In the Middle Watch.
A Voyage to the Cape.
A Book for the Hammock.
The Mystery of the ‘Ocean Star.’
The Romance of Jenny Harlowe.
An Ocean Tragedy.
My Shipmate Louise.
Alone on Wide Wide Sea.
Good Ship ‘Mohock.’
The Phantom Death.
Is He the Man?
Heart of Oak.
The Convict Ship.
The Tale of the Ten.
The Last Entry.

By DORA RUSSELL.
A Country Sweetheart.

By GEORGE AUGUSTUS SALA.


Gaslight and Daylight.

By GEORGE R. SIMS.
The Ring o’ Bells.
Mary Jane’s Memoirs.
Mary Jane Married.
Tales of To-day.
Dramas of Life.
Tinkletop’s Crime.
My Two Wives.
Zeph.
Memoirs of a Landlady.
Scenes from the Show.
The 10 Commandments.
Dagonet Abroad.
Rogues and Vagabonds.

By ARTHUR SKETCHLEY.
A Match in the Dark.
By HAWLEY SMART.
Without Love or Licence.
Beatrice and Benedick.
The Master of Rathkelly.
The Plunger.
Long Odds.

By T. W. SPEIGHT.
The Mysteries of Heron Dyke.
The Golden Hoop.
Hoodwinked.
By Devious Ways.
Back to Life.
The Loudwater Tragedy.
Burgo’s Romance.
Quittance in Full.
A Husband from the Sea.

By ALAN ST. AUBYN.


A Fellow of Trinity.
The Junior Dean.
Master of St. Benedict’s.
To His Own Master.
Orchard Damerel.
In the Face of the World.
The Tremlett Diamonds.

By R. A. STERNDALE.
The Afghan Knife.

By R. LOUIS STEVENSON.
New Arabian Nights.

By BERTHA THOMAS.
Cressida.
The Violin-Player.

By WALTER THORNBURY.
Tales for the Marines.
Old Stories Retold.

By T. ADOLPHUS TROLLOPE.
Diamond Cut Diamond.

By F. ELEANOR TROLLOPE.
Like Ships upon the Sea.
Anne Furness.
Mabel’s Progress.

By ANTHONY TROLLOPE.
Frau Frohmann.
Marion Fay.
Kept in the Dark.
John Caldigate.
The Way We Live Now.
The Land-Leaguers.
The American Senator.
Mr. Scarborough’s Family.
Golden Lion of Granpere.

By J. T. TROWBRIDGE.
Farnell’s Folly.
Welcome to our website – the ideal destination for book lovers and
knowledge seekers. With a mission to inspire endlessly, we offer a
vast collection of books, ranging from classic literary works to
specialized publications, self-development books, and children's
literature. Each book is a new journey of discovery, expanding
knowledge and enriching the soul of the reade

Our website is not just a platform for buying books, but a bridge
connecting readers to the timeless values of culture and wisdom. With
an elegant, user-friendly interface and an intelligent search system,
we are committed to providing a quick and convenient shopping
experience. Additionally, our special promotions and home delivery
services ensure that you save time and fully enjoy the joy of reading.

Let us accompany you on the journey of exploring knowledge and


personal growth!

textbookfull.com

You might also like