Skip to content

Commit 45b01c2

Browse files
committed
2 parents 36af827 + 03a4251 commit 45b01c2

33 files changed

+1022
-82
lines changed

.devcontainer/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# https://github.com/microsoft/vscode-dev-containers/blob/main/containers/python-3/README.md
2-
ARG VARIANT=3.12-bookworm
2+
ARG VARIANT=3.13-bookworm
33
FROM mcr.microsoft.com/vscode/devcontainers/python:${VARIANT}
44
COPY requirements.txt /tmp/pip-tmp/
55
RUN python3 -m pip install --upgrade pip \

.devcontainer/devcontainer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// Update 'VARIANT' to pick a Python version: 3, 3.11, 3.10, 3.9, 3.8
88
// Append -bullseye or -buster to pin to an OS version.
99
// Use -bullseye variants on local on arm64/Apple Silicon.
10-
"VARIANT": "3.12-bookworm",
10+
"VARIANT": "3.13-bookworm",
1111
}
1212
},
1313

.github/CODEOWNERS

-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99

1010
/.* @cclauss
1111

12-
# /arithmetic_analysis/
13-
1412
# /backtracking/
1513

1614
# /bit_manipulation/

.github/workflows/build.yml

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ jobs:
2525
- name: Run tests
2626
# TODO: #8818 Re-enable quantum tests
2727
run: pytest
28-
--ignore=quantum/q_fourier_transform.py
2928
--ignore=computer_vision/cnn_classification.py
29+
--ignore=docs/conf.py
3030
--ignore=dynamic_programming/k_means_clustering_tensorflow.py
3131
--ignore=machine_learning/lstm/lstm_prediction.py
3232
--ignore=neural_network/input_data.py
3333
--ignore=project_euler/
34+
--ignore=quantum/q_fourier_transform.py
3435
--ignore=scripts/validate_solutions.py
3536
--cov-report=term-missing:skip-covered
3637
--cov=. .

.github/workflows/sphinx.yml

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: sphinx
2+
3+
on:
4+
# Triggers the workflow on push or pull request events but only for the "master" branch
5+
push:
6+
branches: ["master"]
7+
pull_request:
8+
branches: ["master"]
9+
# Or manually from the Actions tab
10+
workflow_dispatch:
11+
12+
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
13+
permissions:
14+
contents: read
15+
pages: write
16+
id-token: write
17+
18+
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
19+
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
20+
concurrency:
21+
group: "pages"
22+
cancel-in-progress: false
23+
24+
jobs:
25+
build_docs:
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@v4
29+
- uses: actions/setup-python@v5
30+
with:
31+
python-version: 3.13
32+
allow-prereleases: true
33+
- run: pip install --upgrade pip
34+
- run: pip install myst-parser sphinx-autoapi sphinx-pyproject
35+
- uses: actions/configure-pages@v5
36+
- run: sphinx-build -c docs . docs/_build/html
37+
- uses: actions/upload-pages-artifact@v3
38+
with:
39+
path: docs/_build/html
40+
41+
deploy_docs:
42+
environment:
43+
name: github-pages
44+
url: ${{ steps.deployment.outputs.page_url }}
45+
if: github.event_name != 'pull_request'
46+
needs: build_docs
47+
runs-on: ubuntu-latest
48+
steps:
49+
- uses: actions/deploy-pages@v4
50+
id: deployment

.pre-commit-config.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
repos:
22
- repo: https://github.com/pre-commit/pre-commit-hooks
3-
rev: v4.6.0
3+
rev: v5.0.0
44
hooks:
55
- id: check-executables-have-shebangs
66
- id: check-toml
@@ -16,7 +16,7 @@ repos:
1616
- id: auto-walrus
1717

1818
- repo: https://github.com/astral-sh/ruff-pre-commit
19-
rev: v0.6.8
19+
rev: v0.6.9
2020
hooks:
2121
- id: ruff
2222
- id: ruff-format
@@ -29,7 +29,7 @@ repos:
2929
- tomli
3030

3131
- repo: https://github.com/tox-dev/pyproject-fmt
32-
rev: "2.2.4"
32+
rev: "2.3.0"
3333
hooks:
3434
- id: pyproject-fmt
3535

CONTRIBUTING.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ pre-commit run --all-files --show-diff-on-failure
7777

7878
We want your work to be readable by others; therefore, we encourage you to note the following:
7979

80-
- Please write in Python 3.12+. For instance: `print()` is a function in Python 3 so `print "Hello"` will *not* work but `print("Hello")` will.
80+
- Please write in Python 3.13+. For instance: `print()` is a function in Python 3 so `print "Hello"` will *not* work but `print("Hello")` will.
8181
- Please focus hard on the naming of functions, classes, and variables. Help your reader by using __descriptive names__ that can help you to remove redundant comments.
8282
- Single letter variable names are *old school* so please avoid them unless their life only spans a few lines.
8383
- Expand acronyms because `gcd()` is hard to understand but `greatest_common_divisor()` is not.
@@ -96,7 +96,7 @@ We want your work to be readable by others; therefore, we encourage you to note
9696

9797
```bash
9898
python3 -m pip install ruff # only required the first time
99-
ruff .
99+
ruff check
100100
```
101101

102102
- Original code submission require docstrings or comments to describe your work.

DIRECTORY.md

+9
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
* [Rat In Maze](backtracking/rat_in_maze.py)
2323
* [Sudoku](backtracking/sudoku.py)
2424
* [Sum Of Subsets](backtracking/sum_of_subsets.py)
25+
* [Word Break](backtracking/word_break.py)
26+
* [Word Ladder](backtracking/word_ladder.py)
2527
* [Word Search](backtracking/word_search.py)
2628

2729
## Bit Manipulation
@@ -98,6 +100,7 @@
98100
* [Elgamal Key Generator](ciphers/elgamal_key_generator.py)
99101
* [Enigma Machine2](ciphers/enigma_machine2.py)
100102
* [Fractionated Morse Cipher](ciphers/fractionated_morse_cipher.py)
103+
* [Gronsfeld Cipher](ciphers/gronsfeld_cipher.py)
101104
* [Hill Cipher](ciphers/hill_cipher.py)
102105
* [Mixed Keyword Cypher](ciphers/mixed_keyword_cypher.py)
103106
* [Mono Alphabetic Ciphers](ciphers/mono_alphabetic_ciphers.py)
@@ -210,6 +213,7 @@
210213
* [Lazy Segment Tree](data_structures/binary_tree/lazy_segment_tree.py)
211214
* [Lowest Common Ancestor](data_structures/binary_tree/lowest_common_ancestor.py)
212215
* [Maximum Fenwick Tree](data_structures/binary_tree/maximum_fenwick_tree.py)
216+
* [Maximum Sum Bst](data_structures/binary_tree/maximum_sum_bst.py)
213217
* [Merge Two Binary Trees](data_structures/binary_tree/merge_two_binary_trees.py)
214218
* [Mirror Binary Tree](data_structures/binary_tree/mirror_binary_tree.py)
215219
* [Non Recursive Segment Tree](data_structures/binary_tree/non_recursive_segment_tree.py)
@@ -283,6 +287,7 @@
283287
* [Dijkstras Two Stack Algorithm](data_structures/stacks/dijkstras_two_stack_algorithm.py)
284288
* [Infix To Postfix Conversion](data_structures/stacks/infix_to_postfix_conversion.py)
285289
* [Infix To Prefix Conversion](data_structures/stacks/infix_to_prefix_conversion.py)
290+
* [Lexicographical Numbers](data_structures/stacks/lexicographical_numbers.py)
286291
* [Next Greater Element](data_structures/stacks/next_greater_element.py)
287292
* [Postfix Evaluation](data_structures/stacks/postfix_evaluation.py)
288293
* [Prefix Evaluation](data_structures/stacks/prefix_evaluation.py)
@@ -346,6 +351,9 @@
346351
* [Power](divide_and_conquer/power.py)
347352
* [Strassen Matrix Multiplication](divide_and_conquer/strassen_matrix_multiplication.py)
348353

354+
## Docs
355+
* [Conf](docs/conf.py)
356+
349357
## Dynamic Programming
350358
* [Abbreviation](dynamic_programming/abbreviation.py)
351359
* [All Construct](dynamic_programming/all_construct.py)
@@ -1200,6 +1208,7 @@
12001208
* [Binary Tree Traversal](searches/binary_tree_traversal.py)
12011209
* [Double Linear Search](searches/double_linear_search.py)
12021210
* [Double Linear Search Recursion](searches/double_linear_search_recursion.py)
1211+
* [Exponential Search](searches/exponential_search.py)
12031212
* [Fibonacci Search](searches/fibonacci_search.py)
12041213
* [Hill Climbing](searches/hill_climbing.py)
12051214
* [Interpolation Search](searches/interpolation_search.py)

LICENSE.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
MIT License
1+
## MIT License
22

33
Copyright (c) 2016-2022 TheAlgorithms and contributors
44

backtracking/word_break.py

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
"""
2+
Word Break Problem is a well-known problem in computer science.
3+
Given a string and a dictionary of words, the task is to determine if
4+
the string can be segmented into a sequence of one or more dictionary words.
5+
6+
Wikipedia: https://en.wikipedia.org/wiki/Word_break_problem
7+
"""
8+
9+
10+
def backtrack(input_string: str, word_dict: set[str], start: int) -> bool:
11+
"""
12+
Helper function that uses backtracking to determine if a valid
13+
word segmentation is possible starting from index 'start'.
14+
15+
Parameters:
16+
input_string (str): The input string to be segmented.
17+
word_dict (set[str]): A set of valid dictionary words.
18+
start (int): The starting index of the substring to be checked.
19+
20+
Returns:
21+
bool: True if a valid segmentation is possible, otherwise False.
22+
23+
Example:
24+
>>> backtrack("leetcode", {"leet", "code"}, 0)
25+
True
26+
27+
>>> backtrack("applepenapple", {"apple", "pen"}, 0)
28+
True
29+
30+
>>> backtrack("catsandog", {"cats", "dog", "sand", "and", "cat"}, 0)
31+
False
32+
"""
33+
34+
# Base case: if the starting index has reached the end of the string
35+
if start == len(input_string):
36+
return True
37+
38+
# Try every possible substring from 'start' to 'end'
39+
for end in range(start + 1, len(input_string) + 1):
40+
if input_string[start:end] in word_dict and backtrack(
41+
input_string, word_dict, end
42+
):
43+
return True
44+
45+
return False
46+
47+
48+
def word_break(input_string: str, word_dict: set[str]) -> bool:
49+
"""
50+
Determines if the input string can be segmented into a sequence of
51+
valid dictionary words using backtracking.
52+
53+
Parameters:
54+
input_string (str): The input string to segment.
55+
word_dict (set[str]): The set of valid words.
56+
57+
Returns:
58+
bool: True if the string can be segmented into valid words, otherwise False.
59+
60+
Example:
61+
>>> word_break("leetcode", {"leet", "code"})
62+
True
63+
64+
>>> word_break("applepenapple", {"apple", "pen"})
65+
True
66+
67+
>>> word_break("catsandog", {"cats", "dog", "sand", "and", "cat"})
68+
False
69+
"""
70+
71+
return backtrack(input_string, word_dict, 0)

backtracking/word_ladder.py

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
"""
2+
Word Ladder is a classic problem in computer science.
3+
The problem is to transform a start word into an end word
4+
by changing one letter at a time.
5+
Each intermediate word must be a valid word from a given list of words.
6+
The goal is to find a transformation sequence
7+
from the start word to the end word.
8+
9+
Wikipedia: https://en.wikipedia.org/wiki/Word_ladder
10+
"""
11+
12+
import string
13+
14+
15+
def backtrack(
16+
current_word: str, path: list[str], end_word: str, word_set: set[str]
17+
) -> list[str]:
18+
"""
19+
Helper function to perform backtracking to find the transformation
20+
from the current_word to the end_word.
21+
22+
Parameters:
23+
current_word (str): The current word in the transformation sequence.
24+
path (list[str]): The list of transformations from begin_word to current_word.
25+
end_word (str): The target word for transformation.
26+
word_set (set[str]): The set of valid words for transformation.
27+
28+
Returns:
29+
list[str]: The list of transformations from begin_word to end_word.
30+
Returns an empty list if there is no valid
31+
transformation from current_word to end_word.
32+
33+
Example:
34+
>>> backtrack("hit", ["hit"], "cog", {"hot", "dot", "dog", "lot", "log", "cog"})
35+
['hit', 'hot', 'dot', 'lot', 'log', 'cog']
36+
37+
>>> backtrack("hit", ["hit"], "cog", {"hot", "dot", "dog", "lot", "log"})
38+
[]
39+
40+
>>> backtrack("lead", ["lead"], "gold", {"load", "goad", "gold", "lead", "lord"})
41+
['lead', 'lead', 'load', 'goad', 'gold']
42+
43+
>>> backtrack("game", ["game"], "code", {"came", "cage", "code", "cade", "gave"})
44+
['game', 'came', 'cade', 'code']
45+
"""
46+
47+
# Base case: If the current word is the end word, return the path
48+
if current_word == end_word:
49+
return path
50+
51+
# Try all possible single-letter transformations
52+
for i in range(len(current_word)):
53+
for c in string.ascii_lowercase: # Try changing each letter
54+
transformed_word = current_word[:i] + c + current_word[i + 1 :]
55+
if transformed_word in word_set:
56+
word_set.remove(transformed_word)
57+
# Recur with the new word added to the path
58+
result = backtrack(
59+
transformed_word, [*path, transformed_word], end_word, word_set
60+
)
61+
if result: # valid transformation found
62+
return result
63+
word_set.add(transformed_word) # backtrack
64+
65+
return [] # No valid transformation found
66+
67+
68+
def word_ladder(begin_word: str, end_word: str, word_set: set[str]) -> list[str]:
69+
"""
70+
Solve the Word Ladder problem using Backtracking and return
71+
the list of transformations from begin_word to end_word.
72+
73+
Parameters:
74+
begin_word (str): The word from which the transformation starts.
75+
end_word (str): The target word for transformation.
76+
word_list (list[str]): The list of valid words for transformation.
77+
78+
Returns:
79+
list[str]: The list of transformations from begin_word to end_word.
80+
Returns an empty list if there is no valid transformation.
81+
82+
Example:
83+
>>> word_ladder("hit", "cog", ["hot", "dot", "dog", "lot", "log", "cog"])
84+
['hit', 'hot', 'dot', 'lot', 'log', 'cog']
85+
86+
>>> word_ladder("hit", "cog", ["hot", "dot", "dog", "lot", "log"])
87+
[]
88+
89+
>>> word_ladder("lead", "gold", ["load", "goad", "gold", "lead", "lord"])
90+
['lead', 'lead', 'load', 'goad', 'gold']
91+
92+
>>> word_ladder("game", "code", ["came", "cage", "code", "cade", "gave"])
93+
['game', 'came', 'cade', 'code']
94+
"""
95+
96+
if end_word not in word_set: # no valid transformation possible
97+
return []
98+
99+
# Perform backtracking starting from the begin_word
100+
return backtrack(begin_word, [begin_word], end_word, word_set)

ciphers/autokey.py

+16
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ def encrypt(plaintext: str, key: str) -> str:
2424
Traceback (most recent call last):
2525
...
2626
ValueError: plaintext is empty
27+
>>> encrypt("coffee is good as python", "")
28+
Traceback (most recent call last):
29+
...
30+
ValueError: key is empty
31+
>>> encrypt(527.26, "TheAlgorithms")
32+
Traceback (most recent call last):
33+
...
34+
TypeError: plaintext must be a string
2735
"""
2836
if not isinstance(plaintext, str):
2937
raise TypeError("plaintext must be a string")
@@ -80,6 +88,14 @@ def decrypt(ciphertext: str, key: str) -> str:
8088
Traceback (most recent call last):
8189
...
8290
TypeError: ciphertext must be a string
91+
>>> decrypt("", "TheAlgorithms")
92+
Traceback (most recent call last):
93+
...
94+
ValueError: ciphertext is empty
95+
>>> decrypt("vvjfpk wj ohvp su ddylsv", 2)
96+
Traceback (most recent call last):
97+
...
98+
TypeError: key must be a string
8399
"""
84100
if not isinstance(ciphertext, str):
85101
raise TypeError("ciphertext must be a string")

0 commit comments

Comments
 (0)