This morning, I attended the "Interview Club" and was asked this question by the
interviewer in front of ~20 FTEs. While I struggled to fully solve it during the
abridged (i.e. 20 minute) timeslot, I completed the problem afterwards.
Here is my solution.
Create a suffix tree from an input string. This implementation uses a stack to
control the flow of the program.
I expected this attempt to be easier than my first attempt, but surprisingly, it
was similarly difficult. It took me ~30-45 minutes to successfully implement
this function, and I'm still not pleased with the final result.
While it took me awhile to implement, this exercise was definitely worth
doing. I think there should be a more elegant way to construct the tree using
maybe a stack, but I couldn't find it.
All of this was part of a larger effort to search a string for a variety of
patterns. The solution is to compile the string into a suffix tree and then
search the suffix tree for each of the patterns.
I'm glad I didn't gloss over this exercise.
Tonight I learned that random sample where each element in the sampling corpus
has an equal likelihood of being chosen is a brand of algorithms known as
"reservoir sampling".
- Implement random.shuffle(..)
- Implement random.choice(..)
Surprisingly, candidates are expected to encounter problems like this during
interviews.
Given an input like "gello" suggest an correction like "hello".
This is a proof-of-concept problem for writing a simplistic auto-correction
algorithm for a mobile device.
This algorithm is pretty interesting because it runs in linear time with respect
to the length of the `corpus` string. It does this by using a sliding window
hash. This hash -- because it's a sliding window -- runs in constant time for
each iteration; we're only adding and subtracting one character each time and
not re-hashing the whole "window".
When our hashes match, only then do we compare the "window" to the
`pattern`. String comparisons are linear because they compare each character to
each character one at a time. But because we only compare strings when are
hashes match (a check which runs in constant time), this spares us the
performance hit.
Firstly, implement a function that adds two arguments together... without using
the `+` operator. I need to drill this problem. Thankfully I took a Coursera
course that taught me how to make a half-adder and a full-adder, but the
recommended solution for this is a bit more difficult.
I was always curious how hashing functions were implemented, so I read about the
"polynomial rolling hash function", and I decided implementing it would be a
good exercise. After writing that, writing a hash table was simple.
Write a function to modify an array of integers in-place such that all of the
zeroes in the array are at the end, and the order of the other integers is not
changed.
This solution operates in O(n) time instead of O(n*log(n)) time, which
surprisingly isn't *that* big of a difference...
Consider a size of n of 10M...
1) ~10s
2) ~0.5s
So, yes, the O(n*log(n)) will take 100x longer to complete, but for an enormous
input size of 10M elements, it can still complete in under a minute. The
difference between that and the second, faster, algorithm, is just 9s.
Write a function that reads a string of compressed XML and outputs the
decompressed version.
Note to self: Now that I'm growing more comfortable writing parsers, I'd like to
become equally comfortable writing pretty-printers.
After a five year hiatus, I decided to attempt to solve the famous N queens
problem again. This time, instead of modeling the chess board using a
`[[Bool]]`, I'm using `[Integer]` where the `Integer` indicates which column has
a queen. This is a bit lighter in RAM.
I could have and should have broken this change into smaller pieces, but when I
came up for air, I had changed too much, and most of the changes are
intermingled. Oh well... this is an exciting change!
Include habits for:
- Morning
- Evening
- Payday (the 25th)
- First of the Month
- First of the Year
Since the Morning and Evening routines might be a bit noisy, I'm excluding them
from the output using a flag, `include{Morning,Evening}`, which I support in the
UI to toggle their visibility.
I made *much* more progress on this app that I expected to today, and I *think*
-- short of supporting a database and a server -- I'm close to
being *completely* finished.
Wahoo!
Instead of accepting `List (String, Int)`, accept `List Strategy` where
`Strategy` defines whether or not the string of selectors should be applied to
the element.
I'm also renaming it `class` so I can just use `Utils.class`; `tailwind` has
little to do with the function itself.
Create UI.elm to house components like `button`, which is a simple HTML button
with `focus:outline-none` applied as a `class`, which is an accessibility
feature that I don't need for this touch-screen application.
I like this pattern more than my more opinionated patterns for UI modules in Elm
where I'd define all of the arguments as a record type (i.e. kwargs).
Use the Google Fonts API to fetch a handwritten font, which gives the app a
modicum of personality. There are more "best practices" ways to do this, such
as:
- Download the font once, and include it in the bundle
- Extend the Tailwind configure to recognize the font
- Ditch the inline <style> block
But I don't need the performance benefits that the first bullet provides. And
the second two bullets are more relevant for a larger application with more than
one font. So I think in this case, the easiest solution is best.
Also:
- Use `container` and `mx-auto` to constrain content for wide screens
Created a small MVP for digitizing my weekly habits. Much more to come.
Lots of things happening:
- Copied the boilerplate to get started
- Added a brief project-level README
- Outlined my ambitions in design.md
See README and design.md for more context on this project.
I haven't updated this list since I was living in Dargow, Germany over the
summer. Now that I've settled down, and I'm situated in the London Bridge area,
I'm updating the list.
In the spirit of Marie Kondo, I'm tidying up!
TL;DR:
- Prefer .envrc `use_nix` and delete all dir-locals.nix files
- Remove ~all references to <nixpkgs>, <unstable>, <depot> and prefer
referencing each with briefcase.third_party.{pkgs,unstable,depot}
- Delete nixBufferFromShell function since I was only using that in
dir-locals.nix files
Unforeseen problem: `buildkite-agent` runs its builds in a separate directory,
so if I want the `nix-build` command to build the newly checked out code, I need
to set <briefcase> to the CWD.
Per the assignment's instructions, the `Shift n` operation should treat
the *entire keyboard* like a cycle and shift that. I was erroneously
treating *each row* like a cycle and shifting those one-by-one.
This change fixes that. In addition, it also:
- Updates README.md with expected inputs and outputs
- Updates test suite
- Adds `split` dependency to {default,shell}.nix