Status: Canonical Reference
Scope: All solution files insolutions/
Last Updated: {{ git_revision_date_localized }}
Created: {{ git_creation_date_localized }}
This document defines the contract for solution files in this repository. All solution files MUST conform to this specification. The test runner, generators, and tooling depend on this contract.
- File Structure
- SOLUTIONS Metadata
- Validation (JUDGE_FUNC / COMPARE_MODE)
- Test Files
- Metadata Layers
- Quick Reference
solutions/{problem_id}_{slug}.py
| Component | Format | Example |
|---|---|---|
problem_id |
4-digit zero-padded LeetCode ID | 0001, 0023, 0994 |
slug |
snake_case problem name | two_sum, merge_k_sorted_lists |
Examples:
solutions/0001_two_sum.pysolutions/0023_merge_k_sorted_lists.pysolutions/0051_n_queens.py
Every solution file MUST contain:
| Element | Required | Description |
|---|---|---|
SOLUTIONS dict |
✅ | Metadata for all solution variants |
| Solution class(es) | ✅ | One or more classes implementing the solution |
solve() function |
✅ | Entry point for stdin/stdout execution |
_runner import |
✅ | For polymorphic dispatch |
# solutions/0001_two_sum.py
from typing import List
from _runner import get_solver
SOLUTIONS = {
"default": {
"class": "Solution",
"method": "twoSum",
"complexity": "O(n) time, O(n) space",
"description": "Single pass with hash map",
},
}
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
seen = {}
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
return []
def solve():
import sys
import json
lines = sys.stdin.read().strip().split('\n')
nums = json.loads(lines[0])
target = int(lines[1])
solver = get_solver(SOLUTIONS)
result = solver.twoSum(nums, target)
print(json.dumps(result, separators=(',', ':')))
if __name__ == "__main__":
solve()When a problem has multiple solution approaches, use separate classes with the same method name:
from typing import List
from _runner import get_solver
SOLUTIONS = {
"default": {
"class": "SolutionHeap",
"method": "mergeKLists",
"complexity": "O(N log k)",
"description": "Min-heap approach",
},
"divide": {
"class": "SolutionDivideConquer",
"method": "mergeKLists",
"complexity": "O(N log k)",
"description": "Divide and conquer",
},
"greedy": {
"class": "SolutionGreedy",
"method": "mergeKLists",
"complexity": "O(N * k)",
"description": "Sequential merge",
},
}
class SolutionHeap:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
...
class SolutionDivideConquer:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
...
class SolutionGreedy:
def mergeKLists(self, lists: List[ListNode]) -> ListNode:
...
def solve():
# ... parse input ...
solver = get_solver(SOLUTIONS)
result = solver.mergeKLists(lists)
print(result)Key Polymorphism Rule: All solution classes MUST implement the same method name (the LeetCode original method name). The runner selects the class via SOLUTION_METHOD environment variable.
The test runner selects solutions via the SOLUTION_METHOD environment variable:
# Run default solution
python runner/test_runner.py 0023
# Run specific solution
python runner/test_runner.py 0023 --method divide
# Run all solutions
python runner/test_runner.py 0023 --all📖 See Test Runner Specification for full CLI reference.
The solve() function uses get_solver() to dispatch:
from _runner import get_solver
def solve():
solver = get_solver(SOLUTIONS) # Returns correct class instance
result = solver.methodName(args) # Natural LeetCode-style callThe following patterns are DEPRECATED and should not be used in new code:
| Pattern | Status | Migration |
|---|---|---|
No SOLUTIONS dictionary |
❌ DEPRECATED | Add SOLUTIONS with class + method |
| Wrapper functions | ❌ DEPRECATED | Use polymorphic classes directly |
| Single class with multiple methods | ❌ DEPRECATED | Split into separate classes |
SOLUTIONS without class field |
❌ DEPRECATED | Add class field to each entry |
invoke_solution() helper |
❌ DEPRECATED | Use get_solver() instead |
globals()[method_name] dispatch |
❌ DEPRECATED | Use get_solver() instead |
Deprecated wrapper pattern (DO NOT USE):
# ❌ DEPRECATED
def solve_a(nums, val):
return SolutionA().removeElement(nums, val)
SOLUTIONS = {
"default": {"method": "solve_a", ...}, # Missing 'class' field
}Solutions SHOULD include structured comments to explain the algorithm, approach, and key insights.
Every solution file SHOULD start with a docstring describing the problem:
"""
Problem: Two Sum
Link: https://leetcode.com/problems/two-sum/
Given an array of integers nums and an integer target, return indices
of the two numbers such that they add up to target.
Constraints:
- 2 <= nums.length <= 10^4
- -10^9 <= nums[i] <= 10^9
- -10^9 <= target <= 10^9
- Only one valid answer exists.
"""Note: The Link field should NOT include /description/ suffix. Use the format: https://leetcode.com/problems/{slug}/
| Field | Required | Description |
|---|---|---|
Problem |
✅ | Problem title |
Link |
✅ | LeetCode URL (format: https://leetcode.com/problems/{slug}/, without /description/ suffix) |
| Description | Recommended | Brief problem statement |
Constraints |
Recommended | Key constraints affecting algorithm choice |
Each solution class SHOULD be preceded by a block comment explaining the approach.
Keep it concise: Block comments should have 3-4 bullet points maximum. Focus on key algorithmic insights, not exhaustive explanations.
No blank line between the comment block and the class definition:
# ============================================================================
# Solution 1: Sliding Window (Optimized with Jump)
# Time: O(n), Space: O(min(n, σ))
# - Each character visited at most twice
# - Uses last-seen-index array for O(1) duplicate detection
# - Direct position jumping instead of incremental contraction
# ============================================================================
class SolutionSlidingWindow: # ← No blank line
...Format:
# ============================================
# Solution {N}: {Approach Name}
# Time: O(?), Space: O(?)
# - {Key insight 1}
# - {Key insight 2}
# - {Key insight 3 (optional)}
# ============================================
class ClassName: # ← No blank line before class/function
| Component | Required | Description |
|---|---|---|
| Solution number & name | ✅ | e.g., Solution 1: Sliding Window |
| Time/Space complexity | ✅ | e.g., Time: O(n), Space: O(n) |
| Bullet points (3-4 max) | ✅ | Key insights only, avoid verbose explanations |
| No blank line | ✅ | Comment block directly followed by class/function |
What NOT to include in block comments:
- ❌ Lengthy "Algorithm Insight" or "State Definition" sections
- ❌ Detailed step-by-step pseudocode
- ❌ Redundant restating of code logic
- ❌ More than 4 bullet points
For DP solutions, place the State/Base case/Transition definitions as class-level comments inside the class body, not in the block comment header.
Format:
# ============================================================================
# Solution 1: Interval DP (Character Printing)
# Time: O(n³), Space: O(n²)
# - Key insight: when s[k] == s[i], extend first print to cover s[k]
# - Preprocess: remove consecutive duplicates
# ============================================================================
class Solution:
# State: dp[i][j] = minimum turns to print s[i:j+1]
# Base case: dp[i][i] = 1 (single char needs 1 turn)
# Transition: dp[i][j] = min(dp[i+1][j] + 1,
# dp[i+1][k-1] + dp[k][j]) for s[k]==s[i]
def strangePrinter(self, s: str) -> int:
...| Location | Content |
|---|---|
| Block comment | Algorithm name, Time/Space complexity, key insights (2-3 bullets) |
| Class-level comment | # State:, # Base case:, # Transition: definitions |
| Inline comments | Brief markers like # Base case, # Transition near code |
Rationale: DP definitions are implementation details that belong close to the code. Block comments should focus on high-level algorithmic insights.
Internal comments within methods are acceptable and encouraged for documenting:
- Key state transitions
- Non-obvious logic or edge case handling
- Invariant maintenance
class Solution:
def maxPathSum(self, root: Optional[TreeNode]) -> int:
self.global_max = float('-inf')
def max_contribution(node: Optional[TreeNode]) -> int:
if not node:
return 0
# Prune negative branches - they can only decrease path sum
left_gain = max(0, max_contribution(node.left))
right_gain = max(0, max_contribution(node.right))
# Path through this node as apex: left → node → right
path_through_here = node.val + left_gain + right_gain
self.global_max = max(self.global_max, path_through_here)
# Return single-branch max to parent (can't fork upward)
return node.val + max(left_gain, right_gain)
max_contribution(root)
return self.global_maxInternal comment guidelines:
| ✅ Good | ❌ Bad |
|---|---|
| Explains WHY (non-obvious reasoning) | Restates WHAT the code does |
| Documents invariant or constraint | Comments obvious operations |
| Marks key algorithmic transitions | Excessive line-by-line comments |
SOLUTIONS = {
"key": {
"class": str, # REQUIRED: Class name
"method": str, # REQUIRED: Method name (LeetCode original)
"complexity": str, # RECOMMENDED: Time/space complexity
"description": str, # RECOMMENDED: Brief description
},
}| Field | Type | Required | Description |
|---|---|---|---|
class |
str |
✅ | Python class name that implements the solution |
method |
str |
✅ | Method name to invoke (must match LeetCode signature) |
complexity |
str |
Recommended | Time and/or space complexity (e.g., "O(n) time, O(1) space") |
description |
str |
Recommended | Brief description of the approach |
"default"key is REQUIRED. This is used when no--methodflag is specified.- Additional keys are optional and represent alternative solution approaches.
The keys in SOLUTIONS are shorthand identifiers used to select solution variants via the --method flag:
| Key | Description | Example |
|---|---|---|
"default" |
Default solution approach (required) | "default" |
"divide" |
Divide and conquer approach | "divide" |
"greedy" |
Greedy algorithm approach | "greedy" |
"heap" |
Heap/priority queue approach | "heap" |
"sets" |
Hash set-based approach | "sets" |
"bitmask" |
Bitmask-based approach | "bitmask" |
"dp" |
Dynamic programming approach | "dp" |
"backtrack" |
Backtracking approach | "backtrack" |
Naming Guidelines:
- Use lowercase, descriptive names
- Keep names short (typically 1-2 words)
- Use snake_case for multi-word names (e.g.,
"sliding_window") - Names should reflect the algorithm or data structure used
Usage:
# Run default solution
python runner/test_runner.py 0023
# Run specific solution by shorthand name
python runner/test_runner.py 0023 --method divide
python runner/test_runner.py 0023 --method greedyThe runner validates SOLUTIONS at load time:
SOLUTIONSdictionary MUST exist"default"key MUST be present- Each entry MUST have
"class"field - Each entry MUST have
"method"field - The class specified in
"class"MUST exist in the module - The method specified in
"method"MUST exist on the class
Validation error example:
❌ [0023] Invalid SOLUTIONS format:
- SOLUTIONS['heap'] missing 'class' field
Expected format:
SOLUTIONS = {
"default": {"class": "Solution", "method": "twoSum", ...},
}
SOLUTIONS = {
"default": {
"class": "SolutionBacktrackSets",
"method": "solveNQueens",
"complexity": "O(N!) time, O(N) space",
"description": "Backtracking with hash sets for O(1) conflict detection",
},
"sets": {
"class": "SolutionBacktrackSets",
"method": "solveNQueens",
"complexity": "O(N!) time, O(N) space",
"description": "Backtracking with hash sets for O(1) conflict detection",
},
"bitmask": {
"class": "SolutionBacktrackBitmask",
"method": "solveNQueens",
"complexity": "O(N!) time, O(N) space",
"description": "Backtracking with bitmask for ultra-fast conflict detection",
},
}For integration with the ontology system, these optional fields may be included:
| Field | Type | Description |
|---|---|---|
api_kernels |
list[str] |
API Kernels used (e.g., ["SubstringSlidingWindow"]) |
patterns |
list[str] |
Patterns applied (e.g., ["sliding_window_unique"]) |
families |
list[str] |
Problem families (e.g., ["substring_search"]) |
data_structures |
list[str] |
Data structures used (e.g., ["hash_map", "deque"]) |
algorithms |
list[str] |
Algorithms/techniques (e.g., ["two_pointers"]) |
tags |
list[str] |
Custom tags for categorization |
The runner validates solution output in this priority order:
| Priority | Mode | Trigger | Description |
|---|---|---|---|
| 1 | JUDGE_FUNC |
JUDGE_FUNC defined in module |
Custom validation function |
| 2 | COMPARE_MODE |
COMPARE_MODE defined in module |
Framework-provided comparators |
| 3 | Exact | Default | String equality comparison |
JUDGE_FUNC = judge # Module-level assignmentdef judge(actual, expected, input_data: str) -> bool:
"""
Custom validation function.
Args:
actual: Program output (parsed if possible, else raw string)
expected: Expected output (parsed if possible, else raw string, or None)
input_data: Raw input string
Returns:
bool: True if answer is correct, False otherwise
"""| Argument | Parsing Behavior |
|---|---|
actual |
Parsed via ast.literal_eval() if valid Python literal; otherwise raw string |
expected |
Parsed via ast.literal_eval() if valid Python literal; otherwise raw string; None if .out file missing |
input_data |
Always raw string (no parsing) |
| Return Value | Meaning |
|---|---|
True |
Test passed |
False |
Test failed |
When .out file is missing:
expectedisNoneJUDGE_FUNCMUST validate using onlyactualandinput_data- This enables validation without precomputed expected outputs
📖 This mode is required for generated tests. See Generator Contract.
Example (N-Queens with judge-only support):
def judge(actual: list, expected, input_data: str) -> bool:
"""Validate N-Queens solution."""
n = int(input_data.strip())
# Known solution counts for N-Queens
KNOWN_COUNTS = {1: 1, 2: 0, 3: 0, 4: 2, 5: 10, 6: 4, 7: 40, 8: 92, 9: 352}
# 1. Verify each board is valid
for board in actual:
if not _is_valid_board(board, n):
return False
# 2. Check no duplicates
unique = set(tuple(row for row in board) for board in actual)
if len(unique) != len(actual):
return False
# 3. Check count
if expected is not None:
return len(actual) == len(expected)
else:
# Judge-only: use known counts
expected_count = KNOWN_COUNTS.get(n)
if expected_count is not None:
return len(actual) == expected_count
return True # Accept if count unknown
JUDGE_FUNC = judgeWhen defining a JUDGE_FUNC, you MAY include a block comment explaining its purpose:
# ============================================
# JUDGE_FUNC - Required for generator support
# ============================================
# Uses brute force O(m+n) merge to compute the correct answer,
# then compares with the solution output.
# ============================================
def judge(actual, expected, input_data: str) -> bool: # ← No blank line
...
JUDGE_FUNC = judgeCOMPARE_MODE = "sorted" # Module-level assignment| Mode | Behavior | Use Case |
|---|---|---|
"exact" |
String equality after normalization | Default; exact answer required |
"sorted" |
Sort lists before comparison | "Return in any order" problems |
"set" |
Set comparison (ignores order and duplicates) | Unique elements, order doesn't matter |
Before comparison, outputs are normalized:
- Strip leading/trailing whitespace
- Remove trailing whitespace from each line
- Normalize line endings
For nested lists (e.g., List[List[str]]):
- Convert inner lists to tuples
- Sort outer list
- Compare sorted results
# Example: N-Queens output
actual = [[".Q..", "...Q"], ["..Q.", "Q..."]]
expected = [["..Q.", "Q..."], [".Q..", "...Q"]]
# After sorting: both become the same → PASSThe runner reports validation mode for each test case:
0051_n_queens_1: ✅ PASS [judge]
0051_n_queens_2: ✅ PASS [judge-only]
0001_two_sum_1: ✅ PASS [exact]
0015_3sum_1: ✅ PASS [sorted]
| Label | Meaning |
|---|---|
[judge] |
JUDGE_FUNC used with .out file present |
[judge-only] |
JUDGE_FUNC used without .out file |
[exact] |
Exact string comparison |
[sorted] |
Sorted comparison |
[set] |
Set comparison |
[skip] |
Skipped (no .out and no JUDGE_FUNC) |
neetcode/
├── tests/ # Static test cases
│ ├── {problem_id}_{slug}_{n}.in # Input file
│ └── {problem_id}_{slug}_{n}.out # Expected output file (optional if JUDGE_FUNC)
│
├── generators/ # Dynamic test generators
│ └── {problem_id}_{slug}.py # Generator module
📖 For generator files, see Generator Contract.
tests/{problem_id}_{slug}_{case_number}.{in|out}
Examples:
tests/0001_two_sum_1.intests/0001_two_sum_1.outtests/0051_n_queens_2.intests/0051_n_queens_2.out
- Plain text, parsed by
solve()function - Line endings: LF or CRLF (both accepted)
- Recommended: Use canonical JSON literal format (one parameter per line)
Canonical Format (Recommended):
| Type | Format | Example |
|---|---|---|
| Integer | Plain number | 42 |
| Float | Plain number | 3.14 |
| Boolean | Lowercase JSON | true, false |
| String | JSON quoted | "hello" |
| Array | JSON literal | [1,2,3] |
| 2D Array | JSON literal | [[1,2],[3,4]] |
Example (0001_two_sum_1.in - Canonical Format):
[2,7,11,15]
9
💡 Migration: Use
python -m codegen migrateto convert existing tests to canonical format.
- Plain text matching
print()output fromsolve() - MUST match exactly (after normalization) unless
COMPARE_MODEorJUDGE_FUNCspecified - Recommended: Use canonical JSON literal format
Output Categories:
| Category | Description | Lines | Example Problem |
|---|---|---|---|
| A | Simple return value | 1 | Two Sum |
| B | Return + modified state | 2+ | Remove Element |
| C | Custom judge required | 1+ | 3Sum |
Category A Example (0001_two_sum_1.out):
[0,1]
Category B Example (0027_remove_element_1.out):
2
[2,2]
(Line 1: return value k, Line 2: nums[:k] for verification)
⚠️ Boolean Output: Use lowercasetrue/false(JSON style), notTrue/False(Python style).
📖 See Test File Format for complete output format specification.
Test files can be automatically generated from LeetCode examples:
# Generate solution skeleton with test files
python -m codegen new 1 --with-tests
# Generate tests for existing solution
python -m codegen.core.test_generator # See module for API.out files are optional when:
JUDGE_FUNCis defined (judge-only mode)
python runner/test_runner.py 0001_two_sum# Static + N generated tests
python runner/test_runner.py 0001_two_sum --generate 10
# Reproducible with seed
python runner/test_runner.py 0001_two_sum --generate 10 --seed 12345📖 See Generator Contract § Running Generated Tests for all options.
| Layer | Location | Scope | Change Frequency |
|---|---|---|---|
| Solution-level | solutions/{problem}.py |
Per-solution variant | Often |
| Problem-level | meta/problems/{problem}.toml |
Per-problem | Moderate |
| Ontology-level | ontology/*.toml |
Global definitions | Rarely |
Located in the solution file itself:
SOLUTIONS = {
"default": {
"class": "Solution", # REQUIRED
"method": "twoSum", # REQUIRED
"complexity": "O(n)", # RECOMMENDED
"description": "Hash map", # RECOMMENDED
},
}
# Optional validation
JUDGE_FUNC = judge # Custom validation
COMPARE_MODE = "sorted" # Comparison modeLocated in meta/problems/{problem_id}_{slug}.toml:
[problem]
id = "0001"
title = "Two Sum"
difficulty = "easy"
url = "https://leetcode.com/problems/two-sum/"
topics = ["array", "hash_table"]
companies = ["google", "amazon", "facebook"]
[[solutions]]
key = "default"
class = "Solution"
method = "twoSum"
complexity = "O(n) time"
api_kernels = []
patterns = ["hash_lookup"]Located in ontology/ directory:
| File | Content |
|---|---|
api_kernels.toml |
Reusable algorithmic cores |
patterns.toml |
Problem-solving patterns |
families.toml |
Problem family definitions |
algorithms.toml |
Algorithm taxonomy |
data_structures.toml |
Data structure taxonomy |
topics.toml |
LeetCode topics |
difficulties.toml |
Difficulty levels |
companies.toml |
Company tags |
roadmaps.toml |
Learning path metadata |
When adding or modifying a solution, verify:
-
SOLUTIONSdictionary exists with"default"key - Each entry has
"class"and"method"fields - Class names match actual classes in file
- Method names match actual methods on classes
- All solution classes implement the same method name
- At least one
.infile exists intests/ -
.outfiles exist ORJUDGE_FUNCis defined - Input format in
.inmatchessolve()parsing logic - Output format in
.outmatchesprint()output
- Generator file exists in
generators/ -
generate()function yields correct format -
JUDGE_FUNCis defined in solution (required for generators) - Edge cases are included in generator
📖 See Generator Contract for generator requirements.
# solutions/{problem_id}_{slug}.py
"""
Problem: {Problem Title}
Link: https://leetcode.com/problems/{slug}/
{Brief problem description}
Constraints:
- {constraint 1}
- {constraint 2}
"""
**Note:** Link format must be `https://leetcode.com/problems/{slug}/` (without `/description/` suffix).
```python
from typing import List
from _runner import get_solver
# ============================================
# Optional: Custom validation
# ============================================
# JUDGE_FUNC = judge # For "any order" or complex validation
# COMPARE_MODE = "sorted" # For simple "any order" problems
# ============================================
# SOLUTIONS metadata (REQUIRED)
# ============================================
SOLUTIONS = {
"default": {
"class": "Solution",
"method": "methodName",
"complexity": "O(?)",
"description": "Brief description",
},
}
# ============================================
# Solution 1: {Approach Name}
# Time: O(?), Space: O(?)
# - {Key insight or implementation detail}
# - {Additional notes}
# ============================================
class Solution: # ← No blank line after comment block
def methodName(self, ...):
...
# ============================================
# Entry point
# ============================================
def solve():
import sys
lines = sys.stdin.read().strip().split('\n')
# Parse input...
solver = get_solver(SOLUTIONS)
result = solver.methodName(...)
print(result)
if __name__ == "__main__":
solve()# Run tests
python runner/test_runner.py {problem} # Default solution
python runner/test_runner.py {problem} --method X # Specific solution
python runner/test_runner.py {problem} --all # All solutions
python runner/test_runner.py {problem} --benchmark # With timing📖 See Test Runner Specification for full CLI reference.
| Document | Content |
|---|---|
| Test File Format | Canonical .in/.out format specification |
| Generator Contract | generate(), generate_for_complexity(), edge cases |
| Test Runner Specification | CLI options, output format, troubleshooting |
| Architecture Migration | Polymorphic pattern migration guide |