Personal notes and implementations for dynamic programming, drawn from LeetCode, CLRS (4th ed.), and Levitin Introduction to the Design and Analysis of Algorithms (3rd ed.). Python 3.12+.
The point of this repo is to compare DP strategies on the same problem. A second implementation (space-optimized, top-down memo, brute force, …) is more useful than a new abstraction.
Keep solvers small and comparable. One spec, several implementations; tests run all of them. When adding a problem, follow this loop.
- Readability counts.
- Docstring: module, function, statement, source, examples, constraints.
- Each public function: the state, the recurrence, then
Time/Spacewith the symbols defined. - Comment the DP transition and any non-obvious cost.
- Keep the logic simple and clean.
- Write the recurrence as one expression when it fits.
- Public API is functions; classes only for data structures.
- Prefer a dummy / index sentinel or an early return over a special-case branch.
- Return
int | Nonewhen the result is impossible or absent; do not use a magic value likeinf. - No unused helpers or shared framework.
- Strategy over abstraction.
- Prefer another solver of the same spec over a shared framework.
- Keep problem variants separate from algorithm variants. Encode problem variants in the type.
- Reliability.
- Types as contracts,
collections.abc.Sequencefor read-only inputs. - PEP 695 generics when a structure needs them.
assertpreconditions.
- Types as contracts,
- Constant factors count.
- Avoid an intermediate slice when a cheaper equivalent exists.
- Rolling state and index-based recursion are first-class variants.
- One test body.
- Parametrize
implover every solver of a spec. - Empty and edge cases first, then book or LeetCode samples.
- Parametrize
poetry sync # env + dependencies from pyproject.toml
poetry run pytest # unit tests (benchmarks off)
poetry run pytest -m benchmark # perf only
poetry run pytest -m "" # everything, as in CI
poetry run flake8 src tests # style lint
poetry run isort src tests # sort imports
poetry run mypy src # type check
./scripts/run.sh # pytest + flake8 + isort + mypyDefault pytest skips @pytest.mark.benchmark cases (-m "not benchmark").
Benchmark inputs stay small enough that brute-force variants can still run.
ch8_1_example1_coin_row.py— Chapter 8.1 Example 1 Coin-rowch8_1_example2_change_making.py— Chapter 8.1 Example 2 Change-makingch8_1_example3_coin_collecting.py— Chapter 8.1 Example 3 Coin-collectingch8_2_knapsack.py— Chapter 8.2 Knapsack
ch14_1_rod_cutting.py— Chapter 14.1 Rod Cutting
p3_longest_substring_without_repeating_characters.py— 3. Longest Substring Without Repeating Charactersp5_longest_palindromic_substring.py— 5. Longest Palindromic Substringp23_merge_k_sorted_lists.py— 23. Merge k Sorted Listsp53_maximum_subarray.py— 53. Maximum Subarrayp139_word_break.py— 139. Word Break