Skip to content

Commit deebab9

Browse files
lufftwclaude
andcommitted
docs(guides): add rigorous solution file requirements to pattern guide
Update Phase 1 with detailed specifications: - Complete solution template matching actual 0496 implementation - File-level docstring requirements (Problem, Link, Examples, Constraints) - SOLUTIONS dict field requirements with validation rules - Solution block comment format (no blank line before class) - JUDGE_FUNC template with _reference helper pattern - solve() function requirements with input/output format docs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 27663fc commit deebab9

1 file changed

Lines changed: 273 additions & 57 deletions

File tree

docs/guides/new-pattern.md

Lines changed: 273 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -98,63 +98,104 @@ python -m codegen new <leetcode_id> --with-tests
9898

9999
### 1.2 Solution File Structure
100100

101-
Every solution file MUST include:
101+
Every solution file MUST follow this exact structure. See [Solution Contract](../contracts/solution-contract.md) for complete specification.
102+
103+
#### Required Elements
104+
105+
| Element | Required | Description |
106+
|---------|----------|-------------|
107+
| File-level docstring || Problem description with Link, Examples, Constraints |
108+
| `from _runner import get_solver` || Required import for polymorphic dispatch |
109+
| `SOLUTIONS` dict || Metadata with `"default"` key required |
110+
| Solution class(es) || One or more classes implementing the solution |
111+
| `JUDGE_FUNC` || Custom validation (required for pattern problems) |
112+
| `solve()` function || Entry point for stdin/stdout execution |
113+
114+
#### Complete Solution Template
102115

103116
```python
104117
# solutions/0496_next_greater_element_i.py
105118
"""
106-
LeetCode 496 - Next Greater Element I
107-
108-
Problem: Given two arrays nums1 and nums2, find the next greater element
109-
for each element in nums1 within nums2.
119+
Problem: Next Greater Element I
120+
Link: https://leetcode.com/problems/next-greater-element-i/
121+
122+
The next greater element of some element x in an array is the first greater
123+
element that is to the right of x in the same array.
124+
You are given two distinct 0-indexed integer arrays nums1 and nums2, where
125+
nums1 is a subset of nums2.
126+
127+
Example 1:
128+
Input: nums1 = [4,1,2], nums2 = [1,3,4,2]
129+
Output: [-1,3,-1]
130+
Explanation: The next greater element for each value of nums1 is as follows:
131+
- 4 is underlined in nums2 = [1,3,4,2]. There is no next greater element, so the answer is -1.
132+
- 1 is underlined in nums2 = [1,3,4,2]. The next greater element is 3.
133+
- 2 is underlined in nums2 = [1,3,4,2]. There is no next greater element, so the answer is -1.
134+
135+
Example 2:
136+
Input: nums1 = [2,4], nums2 = [1,2,3,4]
137+
Output: [3,-1]
138+
139+
Constraints:
140+
- 1 <= nums1.length <= nums2.length <= 1000
141+
- 0 <= nums1[i], nums2[i] <= 10^4
142+
- All integers in nums1 and nums2 are unique.
143+
- All the integers of nums1 also appear in nums2.
110144
111-
Pattern: Monotonic Stack (Next Greater Element)
112-
Complexity: O(n + m) time, O(n) space
145+
Topics: Array, Hash Table, Stack, Monotonic Stack
113146
"""
147+
114148
import json
115149
from typing import List
150+
116151
from _runner import get_solver
117152

118-
# ============================================================
119-
# Solution Metadata
120-
# ============================================================
121153

122154
SOLUTIONS = {
123155
"default": {
124-
"class": "SolutionStack",
156+
"class": "SolutionMonotonicStack",
157+
"method": "nextGreaterElement",
158+
"complexity": "O(n + m) time, O(n) space",
159+
"description": "Monotonic decreasing stack with hash map lookup",
160+
},
161+
"stack": {
162+
"class": "SolutionMonotonicStack",
125163
"method": "nextGreaterElement",
126164
"complexity": "O(n + m) time, O(n) space",
127-
"description": "Monotonic decreasing stack with hash map",
165+
"description": "Monotonic decreasing stack with hash map lookup",
166+
},
167+
"brute": {
168+
"class": "SolutionBruteForce",
169+
"method": "nextGreaterElement",
170+
"complexity": "O(m * n) time, O(1) space",
171+
"description": "Linear scan for each query element",
128172
},
129-
# Add variants here if needed
130173
}
131174

132-
# ============================================================
133-
# Solution Classes
134-
# ============================================================
135-
136-
class SolutionStack:
137-
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
138-
# Implementation here
139-
pass
140-
141-
# ============================================================
142-
# JUDGE_FUNC (Required for generated tests)
143-
# ============================================================
144175

176+
# ============================================================================
177+
# JUDGE_FUNC - Required for generator support
178+
# ============================================================================
145179
def judge(actual, expected, input_data: str) -> bool:
146180
"""
147-
Validate solution output.
181+
Validate result: check if actual output is the correct NGE array.
148182
149183
Args:
150-
actual: Solution output (may be list or string)
151-
expected: Expected output from .out file (None for generated tests)
152-
input_data: Raw input string from .in file
184+
actual: Program output (list as string or list)
185+
expected: Expected output (None if from generator)
186+
input_data: Raw input string (nums1 and nums2 on separate lines)
153187
154188
Returns:
155-
bool: True if output is correct
189+
bool: True if correct NGE results
156190
"""
157-
# Parse actual (handle both list and string types)
191+
lines = input_data.strip().split("\n")
192+
nums1 = json.loads(lines[0]) if lines[0] else []
193+
nums2 = json.loads(lines[1]) if len(lines) > 1 else []
194+
195+
# Compute correct answer using reference solution
196+
correct = _reference_nge(nums1, nums2)
197+
198+
# Parse actual output (may be list or string)
158199
if isinstance(actual, list):
159200
actual_list = actual
160201
else:
@@ -164,57 +205,232 @@ def judge(actual, expected, input_data: str) -> bool:
164205
except (ValueError, json.JSONDecodeError):
165206
return False
166207

167-
# For static tests: compare with expected
168-
if expected is not None:
169-
if isinstance(expected, list):
170-
return actual_list == expected
171-
expected_str = expected.strip()
172-
try:
173-
expected_list = json.loads(expected_str) if expected_str else []
174-
except (ValueError, json.JSONDecodeError):
175-
return False
176-
return actual_list == expected_list
208+
return actual_list == correct
177209

178-
# For generated tests: validate using input
179-
lines = input_data.strip().split('\n')
180-
nums1 = json.loads(lines[0])
181-
nums2 = json.loads(lines[1])
182210

183-
# Add validation logic here
184-
return len(actual_list) == len(nums1)
211+
def _reference_nge(nums1: List[int], nums2: List[int]) -> List[int]:
212+
"""O(n + m) reference using monotonic stack."""
213+
nge_map: dict[int, int] = {}
214+
stack: list[int] = []
215+
216+
for num in nums2:
217+
while stack and stack[-1] < num:
218+
nge_map[stack.pop()] = num
219+
stack.append(num)
220+
221+
return [nge_map.get(x, -1) for x in nums1]
222+
185223

186224
JUDGE_FUNC = judge
187225

188-
# ============================================================
189-
# Entry Point
190-
# ============================================================
226+
227+
# ============================================================================
228+
# Solution 1: Monotonic Decreasing Stack + Hash Map
229+
# Time: O(n + m), Space: O(n)
230+
# - Precompute NGE for all elements in nums2 using monotonic stack
231+
# - Stack stores indices of candidates awaiting their next greater element
232+
# - When a larger element appears, it becomes NGE for all smaller candidates
233+
# - Hash map enables O(1) lookup for nums1 queries
234+
#
235+
# Key Insight: The stack maintains a decreasing sequence of unresolved elements.
236+
# When we encounter a larger element, it "resolves" all smaller elements on top.
237+
# ============================================================================
238+
class SolutionMonotonicStack:
239+
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
240+
next_greater_map: dict[int, int] = {}
241+
candidate_stack: list[int] = [] # Stores values (not indices) since unique
242+
243+
# Build NGE map: process nums2 to find next greater for each element
244+
for current_value in nums2:
245+
# Resolve all candidates that found their next greater element
246+
while candidate_stack and candidate_stack[-1] < current_value:
247+
resolved_value = candidate_stack.pop()
248+
next_greater_map[resolved_value] = current_value
249+
250+
# Current element becomes a new candidate awaiting its NGE
251+
candidate_stack.append(current_value)
252+
253+
# Elements remaining in stack have no next greater element
254+
# They will return -1 via dict.get() default
255+
256+
# Look up NGE for each query element
257+
return [next_greater_map.get(query, -1) for query in nums1]
258+
259+
260+
# ============================================================================
261+
# Solution 2: Brute Force Linear Scan
262+
# Time: O(m * n), Space: O(1)
263+
# - For each element in nums1, find its position in nums2
264+
# - Scan right from that position to find the first greater element
265+
# - Simple but inefficient for large inputs
266+
#
267+
# Educational Value: Establishes baseline before optimization.
268+
# ============================================================================
269+
class SolutionBruteForce:
270+
def nextGreaterElement(self, nums1: List[int], nums2: List[int]) -> List[int]:
271+
result: list[int] = []
272+
nums2_length = len(nums2)
273+
274+
for query in nums1:
275+
# Find position of query element in nums2
276+
position = nums2.index(query)
277+
278+
# Scan rightward for next greater element
279+
next_greater = -1
280+
for scan_idx in range(position + 1, nums2_length):
281+
if nums2[scan_idx] > query:
282+
next_greater = nums2[scan_idx]
283+
break
284+
285+
result.append(next_greater)
286+
287+
return result
288+
191289

192290
def solve():
291+
"""
292+
Input format (JSON per line):
293+
Line 1: nums1 as JSON array
294+
Line 2: nums2 as JSON array
295+
296+
Output format:
297+
JSON array of next greater elements
298+
"""
193299
import sys
194-
data = sys.stdin.read().strip().split('\n')
195-
nums1 = json.loads(data[0])
196-
nums2 = json.loads(data[1])
300+
301+
lines = sys.stdin.read().strip().split("\n")
302+
nums1 = json.loads(lines[0])
303+
nums2 = json.loads(lines[1])
197304

198305
solver = get_solver(SOLUTIONS)
199306
result = solver.nextGreaterElement(nums1, nums2)
200-
print(json.dumps(result, separators=(',', ':')))
307+
308+
print(json.dumps(result))
309+
201310

202311
if __name__ == "__main__":
203312
solve()
204313
```
205314

206-
### 1.3 JUDGE_FUNC Requirements
315+
### 1.3 File-Level Docstring Requirements
316+
317+
The docstring MUST include:
318+
319+
| Field | Required | Format |
320+
|-------|----------|--------|
321+
| `Problem:` || Problem title |
322+
| `Link:` || `https://leetcode.com/problems/{slug}/` (NO `/description/` suffix) |
323+
| Description || Problem statement |
324+
| `Example N:` || At least one example with Input/Output/Explanation |
325+
| `Constraints:` || All LeetCode constraints |
326+
| `Topics:` | Recommended | LeetCode topic tags |
327+
328+
### 1.4 SOLUTIONS Dict Requirements
329+
330+
| Rule | Requirement |
331+
|------|-------------|
332+
| `"default"` key | ✅ REQUIRED - used when no `--method` flag specified |
333+
| `"class"` field | ✅ REQUIRED - must match actual class name in file |
334+
| `"method"` field | ✅ REQUIRED - must match LeetCode method signature |
335+
| `"complexity"` field | Recommended - e.g., `"O(n) time, O(n) space"` |
336+
| `"description"` field | Recommended - brief algorithm description |
337+
338+
### 1.5 Solution Block Comment Format
339+
340+
**CRITICAL**: No blank line between comment block and class definition.
341+
342+
```python
343+
# ============================================================================
344+
# Solution N: {Approach Name}
345+
# Time: O(?), Space: O(?)
346+
# - {Key insight 1}
347+
# - {Key insight 2}
348+
# - {Implementation detail}
349+
#
350+
# {Optional extended explanation}
351+
# ============================================================================
352+
class SolutionName: # ← NO blank line here
353+
def methodName(self, ...):
354+
...
355+
```
356+
357+
### 1.6 JUDGE_FUNC Requirements
207358

208359
The `JUDGE_FUNC` is **mandatory** for pattern problems. Key requirements:
209360

210361
| Requirement | Description |
211362
|-------------|-------------|
212363
| Handle both types | `actual` may be `list` or `str` depending on context |
213364
| Support `expected=None` | Generated tests have no expected output |
214-
| Parse `input_data` | Use input to validate correctness |
365+
| Reference solution | Include `_reference_{name}()` helper to compute correct answer |
366+
| Parse `input_data` | Use `json.loads()` to parse input |
215367
| Return boolean | `True` for pass, `False` for fail |
216368

217-
> **Reference**: [Solution Contract](../contracts/solution-contract.md#judge_func-specification)
369+
#### JUDGE_FUNC Template
370+
371+
```python
372+
def judge(actual, expected, input_data: str) -> bool:
373+
"""Validate result."""
374+
# 1. Parse input
375+
lines = input_data.strip().split("\n")
376+
param1 = json.loads(lines[0])
377+
param2 = json.loads(lines[1]) if len(lines) > 1 else None
378+
379+
# 2. Compute correct answer using reference
380+
correct = _reference_solution(param1, param2)
381+
382+
# 3. Parse actual (handle both list and string)
383+
if isinstance(actual, list):
384+
actual_list = actual
385+
else:
386+
actual_str = actual.strip()
387+
try:
388+
actual_list = json.loads(actual_str) if actual_str else []
389+
except (ValueError, json.JSONDecodeError):
390+
return False
391+
392+
# 4. Compare
393+
return actual_list == correct
394+
395+
396+
def _reference_solution(param1, param2):
397+
"""Reference implementation for validation."""
398+
# Implement correct algorithm here
399+
pass
400+
401+
402+
JUDGE_FUNC = judge
403+
```
404+
405+
### 1.7 solve() Function Requirements
406+
407+
```python
408+
def solve():
409+
"""
410+
Input format (JSON per line):
411+
Line 1: {param1 description}
412+
Line 2: {param2 description}
413+
414+
Output format:
415+
{output description}
416+
"""
417+
import sys
418+
419+
lines = sys.stdin.read().strip().split("\n")
420+
param1 = json.loads(lines[0])
421+
param2 = json.loads(lines[1])
422+
423+
solver = get_solver(SOLUTIONS)
424+
result = solver.methodName(param1, param2)
425+
426+
print(json.dumps(result)) # Use json.dumps for arrays
427+
428+
429+
if __name__ == "__main__":
430+
solve()
431+
```
432+
433+
> **Reference**: [Solution Contract](../contracts/solution-contract.md)
218434
219435
---
220436

0 commit comments

Comments
 (0)