From 782566d84b2cb7acf25bedb2dc09dc7f5d948596 Mon Sep 17 00:00:00 2001 From: Peter Chen Date: Sat, 24 Oct 2026 11:29:57 +0800 Subject: [PATCH] chore: add pydoc and update-docs Claude skills - pydoc: write English docstrings with detailed time and space complexity analysis for the functions in a given file - update-docs: sync README.md and docs/ with uncommitted problem changes, verifying LeetCode links via web search Co-Authored-By: Claude Sonnet 5.5 --- .claude/skills/pydoc/SKILL.md | 68 +++++++++++++++++++++++++++ .claude/skills/update-docs/SKILL.md | 72 +++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 .claude/skills/pydoc/SKILL.md create mode 100644 .claude/skills/update-docs/SKILL.md diff --git a/.claude/skills/pydoc/SKILL.md b/.claude/skills/pydoc/SKILL.md new file mode 100644 index 0000000..4afa326 --- /dev/null +++ b/.claude/skills/pydoc/SKILL.md @@ -0,0 +1,68 @@ +--- +name: pydoc +description: Add English docstrings (with detailed time and space complexity analysis) to every function/method in a given Python file. Use when the user runs /pydoc or asks to write pydoc/docstrings for a specific file. +argument-hint: +--- + +# pydoc + +Write English docstrings for the functions in the file given as the argument (`$ARGUMENTS`). + +## Steps + +1. Resolve the file path (relative to the repo root if not absolute). If it does not exist or is not a `.py` file, tell the user and stop. +2. Read the whole file. Understand what each function does before writing. +3. For every function/method that does not already have a docstring (including helper/nested functions that contain real logic), add one using the format below. Skip `__init__.py` boilerplate and `test_*` functions in test files unless the user asks. + - If a function already has a docstring, keep it unless it lacks complexity analysis; in that case, extend it rather than rewrite it. +4. Do not change any code, signatures, comments (such as `# Tags:` lines), or formatting outside the docstrings. +5. Run `python -m py_compile ` to confirm the file still parses. If a test file sits next to it, run it with `python -m pytest -q` as well. +6. Report briefly which functions were documented and the complexity stated for each. + +## Docstring format + +Place the docstring as the first statement inside the function body (PEP 257), using `"""` and matching the surrounding indentation. Follow the style already used in this repo: + +```python +def are_occurrences_equal(self, s: str) -> bool: + """ + Check whether every distinct character in s occurs the same number of times. + + Args: + s: Input string. + + Returns: + True if all characters have equal frequency, otherwise False. + + Time complexity: O(n) + - n is the length of s, k is the number of distinct characters (k <= n) + - Counter(s) scans the string once and each insertion/update is O(1) + on average: O(n) + - count_table.values() iterates over k counts: O(k) + - set(...) hashes each of the k counts: O(k) + - len(...) and the comparison with 1 are O(1) + - Total: O(n) + O(k) + O(k) = O(n), since k <= n + - Worst case: hash collisions could degrade dict operations, but this is + not a practical concern for single characters + + Space complexity: O(k) + - Counter stores one entry per distinct character: O(k) + - The set of frequencies holds at most k values (usually far fewer): O(k) + - No recursion, so no call stack overhead + - Total auxiliary space: O(k); k is bounded by the alphabet size + (e.g. 26 for lowercase letters), so it is O(1) if the alphabet is fixed + """ +``` + +Rules: +- Written in English. First line: a one-sentence summary in imperative mood. +- Include `Args:` and `Returns:` (and `Raises:` only if the code raises). Omit `Args:` for functions without parameters (other than `self`). +- **Always** include both `Time complexity:` and `Space complexity:` with Big-O. +- Make the complexity analysis detailed and step by step: + - Define every variable first (n, m, k, h, ...) and any relationship between them (e.g. k <= n). + - Break the cost down line by line / operation by operation (each loop, sort, hash lookup, slice, copy, string concatenation, built-in call such as `sorted`, `set`, `Counter`, `heapq`, `in` on list vs. set), then show how the parts sum to the final result and why lower-order terms are dropped. + - Cover best / average / worst case when they differ (early return, hash collisions, unbalanced tree, quicksort-style pivots, amortized costs such as list append or dynamic resizing). + - For loops, say how many iterations occur and the cost of each; for nested loops, multiply and explain why; for recursion, give the recurrence or the depth times the per-call work. + - For space, separately account for: data structures built, recursion call stack depth, slices/copies created, and the output. State clearly whether the output is counted. Mention when space is bounded by a fixed alphabet or constraint (e.g. O(1) for 26 letters). + - If a simpler or tighter bound exists under the problem constraints, mention it. +- Be accurate, not optimistic. Verify every claim against the actual code; don't copy complexity from the problem's editorial if the code differs. +- Don't restate the code line by line outside the complexity sections; keep summary, Args and Returns concise. diff --git a/.claude/skills/update-docs/SKILL.md b/.claude/skills/update-docs/SKILL.md new file mode 100644 index 0000000..936b04b --- /dev/null +++ b/.claude/skills/update-docs/SKILL.md @@ -0,0 +1,72 @@ +--- +name: update-docs +description: Update README.md and the files under docs/ (EASY.md, MEDIUM.md, HARD.md) to reflect the current uncommitted changes (newly added, migrated, renamed or removed problems). Use when the user runs /update-docs or asks to sync the README/docs with their uncommitted changes. +--- + +# update-docs + +Sync `README.md` and `docs/*.md` with the problems added, removed or renamed in the **uncommitted** changes (staged + unstaged + untracked). + +## Steps + +1. **Find the uncommitted changes.** + - Run `git status --porcelain` (include untracked files) and `git diff HEAD --name-status`. + - Only care about problem directories matching `problems/{easy,medium,hard}/_/`. Ignore changes to other files (tests/, utils/, docs/, README.md itself, `__pycache__`). + - Classify each affected problem directory: + - **Added**: `solution.py` is new (status `A` / `??`) and the directory is not yet listed in `docs/`. + - **Removed**: the directory (or its `solution.py`) is deleted. + - **Renamed/moved**: the directory moved (e.g. changed difficulty or name); treat as remove old + add new. + - **Modified only**: a content change to an already-listed problem needs no docs change. Skip it. + - Legacy files deleted from the old structure (e.g. `Hash Table/2351. First Letter to Appear Twice.py`) are part of a migration; they don't have their own docs row, so ignore them. + - If there are no relevant problem changes, say so and stop without editing anything. + +2. **Gather the facts for each added problem.** + - Difficulty: from the parent directory (`easy` / `medium` / `hard`). + - Number: the trailing `_` of the directory name. + - Slug: **derive it from the directory name**: + 1. strip the trailing problem number (`_2258`) + 2. replace underscores with hyphens + 3. lowercase everything + + | Directory | Slug | + |-----------|------| + | `escape_the_spreading_fire_2258` | `escape-the-spreading-fire` | + | `first_letter_to_appear_twice_2351` | `first-letter-to-appear-twice` | + | `binary_search_704` | `binary-search` | + - Title: the official LeetCode title for that problem number (e.g. "Escape the Spreading Fire"), used as the link text. Take it from the search results that confirm the link (step 3). Never guess it. + - Confirm that both `solution.py` and `test_solution.py` exist in the directory; link only the files that exist. + +3. **Verify every new link.** The directory name is not guaranteed to match LeetCode's real slug: names may be abbreviated or CamelCase (e.g. `CheckIfAllCharactersHaveEqualNumberOfOccurrences1941`), and LeetCode drops apostrophes instead of turning them into separators (the real slug of "Check if All A's Appears Before All B's" is `check-if-all-as-appears-before-all-bs`, while the directory `check_if_all_a_s_appears_before_all_b_s_2124` would give `check-if-all-a-s-appears-before-all-b-s`). So always confirm: + - Do not fetch leetcode.com directly: it blocks WebFetch with HTTP 403. Use WebSearch instead (load it via ToolSearch `select:WebSearch` if needed), with a query like `leetcode `. + - In the results, look for a `leetcode.com/problems/<slug>/` URL whose problem number and title match, and use the slug from that URL. If it differs from the directory-derived slug, the search result wins. + - Take the official title (the link text) from the same results. + - If the search results do not show a matching `leetcode.com/problems/...` URL, do not guess: ask the user for the correct link, or add the row with the directory-derived slug and clearly flag it as **unverified** in the final report. + - Include the confirming leetcode.com URL as a source link in the final report. + +4. **Update `docs/<DIFFICULTY>.md`** (`EASY.md`, `MEDIUM.md`, `HARD.md`). Read the file first and follow its exact format: + - Add one table row per added problem, **sorted ascending by problem number**, in this format (paths are relative to `docs/`; `<dir>` is the real directory name, `<slug>` is the verified slug): + + ``` + | <number> | [<Title>](https://leetcode.com/problems/<slug>/) | [solution.py](../problems/<difficulty>/<dir>/solution.py) | [test_solution.py](../problems/<difficulty>/<dir>/test_solution.py) | + ``` + - Remove rows for removed problems (and for the old location of a moved one). + - Update the `Total: N problem(s) solved` line to the new row count. Use "problem" when N is 1 and "problems" otherwise. + - Do not touch the footer (`---` and the "Back to Main README" link) or any other row. + +5. **Update `README.md`.** + - Recompute the counts from the **actual table rows** in `docs/EASY.md`, `docs/MEDIUM.md` and `docs/HARD.md` (after step 4), not by incrementing the old numbers. + - Update the `📊 **Statistics**: N problems solved (migrated to new structure)` line (N = easy + medium + hard) and the Easy / Medium / Hard counts in the statistics table, keeping the existing column alignment. + - Only change other README sections (features, project structure, migration status) if the uncommitted changes actually invalidate them; otherwise leave them alone. + +6. **Check consistency.** + - For each docs file, check that the `Total:` number equals the number of table rows, rows are sorted by number with no duplicates, and every linked `solution.py` / `test_solution.py` path exists on disk. + - Check that the README numbers match the docs files. + +7. **Report** what changed: which problems were added/removed, the new totals, how each new LeetCode link was verified (and whether the slug had to be corrected from the directory-derived one), and anything unverified or uncertain. Do not commit; leave the changes in the working tree for the user to review. + +## Rules + +- Only edit `README.md` and files under `docs/`. Never modify solutions or tests. +- Match the existing Markdown style exactly (emoji, table format, pluralization, trailing newline). +- Never guess LeetCode titles or links; verify or ask. +- Be idempotent: running the skill twice in a row must not add duplicate rows or change counts a second time.