From 7273e0301764ce9edf695dc87df14f77f477e2f8 Mon Sep 17 00:00:00 2001 From: Ali Ammari Date: Fri, 12 Jun 2026 02:11:43 +0100 Subject: [PATCH 1/9] chore: relicense to MIT and reconcile README stats Replace the Source-Available "Commercial Use License" with MIT (SPDX-License-Identifier: MIT) plus a footer clarifying that LeetCode problem statements, test data, and trademarks remain LeetCode IP and are not covered. Rework the README: dynamic shields badges sourced from docs/progress.json, a generated PROGRESS block (single source of truth, replacing the self-contradicting 173-vs-544 stats), corrected Oracle/MySQL labelling, accurate SQL example with the // link comment, and new Tooling/Docs/AI sections. Banner referenced as a TODO. Co-Authored-By: Claude Opus 4.8 (1M context) --- LICENSE | 81 +++++++++++++--------------- README.md | 156 ++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 170 insertions(+), 67 deletions(-) diff --git a/LICENSE b/LICENSE index ff9dda0..c08a69d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,45 +1,36 @@ -# COMMERCIAL USE LICENSE - -Copyright (c) 2023-2025 Ali Ammari - -## PERMISSIONS - -You are free to: -- Use, modify, and distribute this code for personal and non-commercial purposes -- Study and learn from this code -- Fork and contribute improvements -- Use in educational settings - -## RESTRICTIONS - -Commercial use is NOT permitted without explicit written permission. - -Commercial use includes: -- Selling or distributing this code or derived products -- Incorporating into paid products or services -- Using to provide commercial services -- Using in profit-generating activities - -## COMMERCIAL USE REQUEST - -For commercial use, contact: ammari.ali.0001@gmail.com - -Subject: "Commercial Use Request - Leetcode_problems" - -Include: -- Your name and organization -- Description of intended use -- Implementation timeline - -## INTELLECTUAL PROPERTY NOTICE - -This project uses third-party assets and libraries. The author does NOT own these assets. Each component retains its original license and copyright. - -Users are responsible for: -- Reviewing all third-party licenses -- Ensuring compliance -- Obtaining separate licenses for commercial use of third-party components if required - -## DISCLAIMER - -THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. +MIT License + +SPDX-License-Identifier: MIT + +Copyright (c) 2023-2026 Ali Ammari + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + +NOTE ON LEETCODE INTELLECTUAL PROPERTY + +The MIT license above applies only to the original solution source code authored +by the repository owner. LeetCode problem statements, test data, problem titles, +problem numbers, and the LeetCode name and trademarks are the intellectual +property of LeetCode (leetcode.com) and are NOT covered by this license. Problem +slugs referenced in this archive (e.g. via the `// https://leetcode.com/problems/` +comments) link back to the original problems on leetcode.com, which remain +subject to LeetCode's own Terms of Service. This repository reproduces only the +author's own submitted solutions, not LeetCode's problem content. diff --git a/README.md b/README.md index a701cf4..4b625be 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,18 @@ # Leetcode Problems -A comprehensive collection of LeetCode problem solutions organized by individual problem directories. This repository serves as a personal coding practice journal and tracks submission history across multiple attempts and languages. + + + +[![Solved](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Faliammari1%2FLeetcode_problems%2Fmain%2Fdocs%2Fprogress.json&query=%24.stats.unique_slugs&label=problems&color=brightgreen)](docs/progress.json) +[![Submissions](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Faliammari1%2FLeetcode_problems%2Fmain%2Fdocs%2Fprogress.json&query=%24.stats.total_files&label=submissions&color=blue)](docs/progress.json) +[![Accepted](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Faliammari1%2FLeetcode_problems%2Fmain%2Fdocs%2Fprogress.json&query=%24.stats.accepted_pct&suffix=%25&label=accepted&color=success)](docs/progress.json) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) + +A LeetCode solution archive organized by individual problem directories. What makes +it different from a typical solutions dump: it preserves the **real submission +history** β€” every Wrong Answer, Runtime Error, Time Limit Exceeded, and Compile +Error attempt that came before the accepted solution, each timestamped β€” across +C++, Java, Oracle SQL, and MySQL. ## πŸš€ Features @@ -12,29 +24,61 @@ A comprehensive collection of LeetCode problem solutions organized by individual ## πŸ“Š Progress Statistics -| Language | Files | Percentage | -|-------------|-------|------------| -| C++ | 496 | 72.7% | -| Java | 18 | 2.6% | -| Oracle SQL | 26 | 3.8% | -| MySQL | 2 | 0.3% | -| **Total Problems** | **173** | **100%** | +> These numbers are generated from a single source of truth β€” +> `scripts/generate_progress.py` walks `solutions/` and reconciles **unique +> problems** vs **total submission files** (the README previously contradicted +> itself on this). CI fails if this block is stale. + + + +- **Unique problems (slugs):** 173 +- **Total submission files:** 542 (every saved attempt across verdicts and timestamps) +- **Accepted problems:** 160 (92.5% of unique problems) + +### Language coverage + +| Language | Files | Problems | % of files | +|----------|-------|----------|-----------| +| C++ | 496 | 146 | 91.5% | +| Java | 18 | 9 | 3.3% | +| Oracle SQL | 26 | 22 | 4.8% | +| MySQL | 2 | 2 | 0.4% | +| **Total** | **542** | **173** | **100%** | -*Last updated: 2025-06-26* +### Submission verdicts + +| Verdict | Files | +|---------|-------| +| Accepted | 277 | +| Wrong Answer | 156 | +| Time Limit Exceeded | 57 | +| Runtime Error | 47 | +| Compile Error | 5 | + +*Generated by `scripts/generate_progress.py`. Do not edit this block by hand.* + + ## πŸ› οΈ Technologies Used -- **Primary Languages**: C++ (72.7%), Java (2.6%) -- **Database Languages**: Oracle SQL, MySQL +- **Primary Languages**: C++ (the bulk of the archive), Java +- **Database Languages**: **Oracle SQL** (`.oraclesql`) and MySQL (`.mysql`). + Note: LeetCode's SQL-50 study plan is MySQL-graded, but most database + solutions here were written and saved as Oracle PL/SQL, so they are linted + with `sqlfluff --dialect oracle` (and the MySQL files with `--dialect mysql`). - **Tools**: VS Code, LeetCode Platform Integration - **Platforms**: LeetCode +See the live counts in [Progress Statistics](#-progress-statistics) above. + ## πŸ“ Repository Structure ``` Leetcode_problems/ -β”œβ”€β”€ assets/ -β”‚ └── repo_image_Leetcode_problems.png +β”œβ”€β”€ assets/ # banner / social-preview assets (see BANNER.md) +β”œβ”€β”€ docs/ # mkdocs-material site (generated per-slug pages) +β”œβ”€β”€ scripts/ +β”‚ └── generate_progress.py # single source of truth for stats + docs β”œβ”€β”€ solutions/ β”‚ β”œβ”€β”€ add-binary/ β”‚ β”‚ └── Accepted/ @@ -54,7 +98,7 @@ Leetcode_problems/ β”‚ β”‚ └── Accepted/ β”‚ β”‚ └── [timestamp]/ β”‚ β”‚ └── Solution.oraclesql -β”‚ └── [173 total problem directories] +β”‚ └── [one directory per problem slug] β”œβ”€β”€ LICENSE └── README.md ``` @@ -69,7 +113,7 @@ Each problem follows this consistent structure: ## 🎯 Problem Categories -Based on the 173 solved problems, this repository covers a wide range of topics: +This repository covers a wide range of topics: ### Data Structures - **Arrays**: Two Sum, Best Time to Buy and Sell Stock, Build Array from Permutation @@ -129,11 +173,16 @@ class Solution { ``` ### SQL Solution Format + +SQL solutions carry the same `//` first-line link comment as the C++/Java files +(the metadata hook the generator parses), followed by the LeetCode editor's own +prompt comment: + ```sql --- https://leetcode.com/problems/combine-two-tables -SELECT firstName, lastName, city, state -FROM person p -LEFT JOIN address a ON p.personId = a.personId; +// https://leetcode.com/problems/article-views-i + +/* Write your PL/SQL query statement below */ +select distinct author_id as "id" from views where author_id = viewer_id order by author_id; ``` ## πŸ“ Submission Status Categories @@ -182,6 +231,60 @@ LEFT JOIN address a ON p.personId = a.personId; -- Copy and paste SQL from Solution.oraclesql into your database client ``` +## πŸ§ͺ Tooling & CI + +Because the saved snippets are bare LeetCode editor bodies with **no +`#include` / `import`**, a naive `g++ Solution.cpp` would compile nothing. CI +therefore uses dedicated gates (all path globbing is **quoted**, since verdict +and timestamp directories contain spaces and commas): + +- **C++ preamble-injection syntax gate** (`scripts/compile_check.py`): for each + Accepted `Solution.cpp`, prepend `#include ` + `using + namespace std;` into a temp file, then run + `g++ -std=c++20 -fsyntax-only -Wall` and fail on real errors. +- **clang-format** (`.clang-format`) + **cpp-linter** on C++ files. +- **sqlfluff** lint: `--dialect oracle` for `.oraclesql`, `--dialect mysql` for + `.mysql`. +- **Progress check**: `generate_progress.py --check` fails CI if the README + stats block or `docs/progress.json` drift from `solutions/`. + +Run the gates locally: + +```bash +python scripts/generate_progress.py --check +python scripts/compile_check.py # needs g++ +clang-format --dry-run --Werror $(git ls-files 'solutions/**/Solution.cpp') +sqlfluff lint --dialect oracle $(git ls-files 'solutions/**/*.oraclesql') +``` + +## πŸ“– Documentation site + +An [mkdocs-material](https://squidfunk.github.io/mkdocs-material/) site is +generated from `solutions/` β€” one page per problem with its **verdict history** +and every language variant in content tabs, plus an index by language. Build it: + +```bash +pip install -r requirements-docs.txt +python scripts/generate_progress.py --docs +mkdocs build --strict +mkdocs serve # local preview +``` + +The site deploys to **Cloudflare Pages** via a gated workflow (see +`.github/workflows/docs.yml`); the deploy step only runs when the +`CLOUDFLARE_API_TOKEN` / `CLOUDFLARE_ACCOUNT_ID` secrets are present. + +## πŸ€– AI tooling + +A workflow (`.github/workflows/ai-explain.yml`, gated on `ANTHROPIC_API_KEY`) +runs `scripts/ai_explain.py`. For each Accepted solution it asks Claude +(`claude-sonnet-4-6`, configurable) to write an `EXPLANATION.md` covering the +approach, complexity, **and a diff/explanation versus the sibling Wrong-Answer / +Runtime-Error attempts** β€” the bug that the earlier submission tripped on. The +docs generator folds these explanations into each problem page. The same script +can auto-tag difficulty/pattern. CodeRabbit / claude-code-action handle PR +review. + ## 🀝 Contributing Contributions are welcome! Here's how you can help: @@ -203,7 +306,9 @@ Contributions are welcome! Here's how you can help: ## πŸ“ˆ Study Plan -### Current Progress: 173 Problems Solved +### Current Progress + +See the live, auto-generated counts in [Progress Statistics](#-progress-statistics). #### Language Distribution - **C++ Focus**: Master data structures and algorithms implementation @@ -222,7 +327,7 @@ Connect with me on LeetCode: [aliammari1](https://leetcode.com/aliammari1/) ## 🎯 Goals -- [x] Solve 100+ problems (173/100+ βœ…) +- [x] Solve 100+ problems βœ… - [x] Master C++ implementation - [x] Learn Java fundamentals - [x] Practice SQL queries @@ -247,7 +352,14 @@ Connect with me on LeetCode: [aliammari1](https://leetcode.com/aliammari1/) ## πŸ“„ License -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. +The original solution source code in this repository is licensed under the **MIT +License** (`SPDX-License-Identifier: MIT`) β€” see [LICENSE](LICENSE). + +> **LeetCode IP:** the MIT license covers only the author's own solution code. +> LeetCode problem statements, test data, titles, numbers, and trademarks belong +> to LeetCode (leetcode.com) and are **not** covered by this license. The slug +> links in each file point back to the original problems, which remain subject to +> LeetCode's Terms of Service. ## πŸ™ Acknowledgments From 8d8f49b6fa216cfea0c900ac557678aff21700ed Mon Sep 17 00:00:00 2001 From: Ali Ammari Date: Fri, 12 Jun 2026 02:12:01 +0100 Subject: [PATCH 2/9] feat: add progress generator and reconcile stats from a single source scripts/generate_progress.py walks solutions////Solution. (pathlib, so no shell quoting needed for the spaces+commas in dir names), parses the first-line `// leetcode.com/problems/` comment, and computes unique slugs (173) vs total submission files (542) plus per-language and per-verdict breakdowns. It regenerates the README PROGRESS block and docs/progress.json (which feeds the dynamic shields badges), and with --docs builds the mkdocs pages. --check fails CI when artifacts drift. Verified: slugs=173, files=542 (496 C++ / 18 Java / 26 Oracle SQL / 2 MySQL), accepted=160 (92.5%). Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 19 ++ docs/progress.json | 60 +++++ scripts/ai_explain.py | 191 +++++++++++++++ scripts/compile_check.py | 120 ++++++++++ scripts/generate_progress.py | 434 +++++++++++++++++++++++++++++++++++ 5 files changed, 824 insertions(+) create mode 100644 .gitignore create mode 100644 docs/progress.json create mode 100644 scripts/ai_explain.py create mode 100644 scripts/compile_check.py create mode 100644 scripts/generate_progress.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e7a15ce --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +# mkdocs build output +/site/ + +# Generated docs pages (rebuilt by scripts/generate_progress.py --docs in CI; +# not committed to avoid churn). Only docs/progress.json is committed so the +# README's dynamic badges resolve without needing a docs build. +/docs/problems/ +/docs/index.md +/docs/tags.md + +# Python +__pycache__/ +*.pyc +.venv/ +venv/ + +# OS +.DS_Store +Thumbs.db diff --git a/docs/progress.json b/docs/progress.json new file mode 100644 index 0000000..3f21862 --- /dev/null +++ b/docs/progress.json @@ -0,0 +1,60 @@ +{ + "schemaVersion": 1, + "label": "solved", + "message": "173 problems", + "color": "brightgreen", + "stats": { + "unique_slugs": 173, + "total_files": 542, + "accepted_slugs": 160, + "accepted_pct": 92.5, + "languages": [ + { + "language": "C++", + "files": 496, + "slugs": 146, + "pct_files": 91.5 + }, + { + "language": "Java", + "files": 18, + "slugs": 9, + "pct_files": 3.3 + }, + { + "language": "Oracle SQL", + "files": 26, + "slugs": 22, + "pct_files": 4.8 + }, + { + "language": "MySQL", + "files": 2, + "slugs": 2, + "pct_files": 0.4 + } + ], + "verdicts": [ + { + "verdict": "Accepted", + "files": 277 + }, + { + "verdict": "Wrong Answer", + "files": 156 + }, + { + "verdict": "Time Limit Exceeded", + "files": 57 + }, + { + "verdict": "Runtime Error", + "files": 47 + }, + { + "verdict": "Compile Error", + "files": 5 + } + ] + } +} diff --git a/scripts/ai_explain.py b/scripts/ai_explain.py new file mode 100644 index 0000000..8b42d0f --- /dev/null +++ b/scripts/ai_explain.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python3 +"""Generate per-solution AI explanations with Claude. + +For each Accepted solution this writes an ``EXPLANATION.md`` next to it covering: + +1. the approach and intuition, +2. time / space complexity, +3. **a diff/explanation versus the sibling Wrong-Answer / Runtime-Error / TLE + attempts for the same problem** β€” what the earlier submission got wrong and + how the accepted one fixes it. That comparison against the real failed + attempts is the unique hook of this archive. + +Optionally (``--tag``) it also emits a one-line difficulty + pattern tag block, +which the docs generator can surface as tags. + +The output feeds the mkdocs page generator (`generate_progress.py --docs`), +which embeds any `EXPLANATION.md` it finds on the problem page. + +Gated on ``ANTHROPIC_API_KEY``: if the key is unset the script prints a notice +and exits 0, so the workflow is a no-op on forks/PRs without the secret. Uses +the official Anthropic Python SDK (``pip install anthropic``). + +Usage:: + + python scripts/ai_explain.py # all Accepted lacking EXPLANATION.md + python scripts/ai_explain.py --slug two-sum # one problem + python scripts/ai_explain.py --force --limit 20 + python scripts/ai_explain.py --model claude-sonnet-4-6 +""" + +from __future__ import annotations + +import argparse +import os +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +SOLUTIONS = ROOT / "solutions" +DEFAULT_MODEL = "claude-sonnet-4-6" + +SYSTEM_PROMPT = ( + "You are a precise competitive-programming reviewer. You write tight, " + "technically accurate explanations of LeetCode solutions for a " + "documentation site. Use Markdown. Do not restate the full problem; assume " + "the reader can follow the linked problem. Be concrete about complexity and " + "about the specific bug in any failed attempt." +) + + +def read_solution(d: Path) -> str | None: + for sol in sorted(d.glob("Solution.*")): + if sol.is_file(): + return sol.read_text(encoding="utf-8", errors="replace") + return None + + +def collect_attempts(slug_dir: Path) -> dict[str, list[str]]: + """Return {verdict: [code, ...]} across all timestamp dirs for a slug.""" + out: dict[str, list[str]] = {} + for verdict_dir in sorted(p for p in slug_dir.iterdir() if p.is_dir()): + for ts_dir in sorted(p for p in verdict_dir.iterdir() if p.is_dir()): + code = read_solution(ts_dir) + if code: + out.setdefault(verdict_dir.name, []).append(code) + return out + + +def build_prompt(slug: str, accepted_code: str, attempts: dict[str, list[str]], tag: bool) -> str: + parts = [ + f"Problem: https://leetcode.com/problems/{slug}/", + "", + "ACCEPTED solution:", + "```", + accepted_code.strip(), + "```", + ] + failed = {v: c for v, c in attempts.items() if v != "Accepted"} + if failed: + parts += ["", "Earlier FAILED attempts on the same problem:"] + for verdict, codes in failed.items(): + for code in codes[:2]: # cap to keep the prompt bounded + parts += [f"", f"[{verdict}]", "```", code.strip(), "```"] + parts += [ + "", + "Write EXPLANATION.md with these sections:", + "## Approach β€” the intuition and the algorithm in a few sentences.", + "## Complexity β€” time and space, with a one-line justification.", + ] + if failed: + parts.append( + "## What the earlier attempts got wrong β€” for each failed verdict " + "above, name the specific bug (off-by-one, wrong data structure, " + "missing edge case, etc.) and how the accepted version fixes it." + ) + else: + parts.append( + "## Notes β€” any edge cases or pitfalls worth flagging (there are no " + "recorded failed attempts for this problem)." + ) + if tag: + parts.append( + "Finally add a line exactly like `Tags: difficulty=, " + "pattern=` based on the solution." + ) + return "\n".join(parts) + + +def main() -> int: + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument("--model", default=os.environ.get("AI_MODEL", DEFAULT_MODEL)) + ap.add_argument("--slug", help="only this slug") + ap.add_argument("--limit", type=int, default=0, help="cap number of slugs processed") + ap.add_argument("--force", action="store_true", help="overwrite existing EXPLANATION.md") + ap.add_argument("--tag", action="store_true", help="also emit difficulty/pattern tags") + args = ap.parse_args() + + api_key = os.environ.get("ANTHROPIC_API_KEY") + if not api_key: + print("ANTHROPIC_API_KEY not set β€” skipping AI explanations (no-op).") + return 0 + + try: + import anthropic + except ImportError: + print("::error::`anthropic` package not installed (pip install anthropic).", file=sys.stderr) + return 2 + + client = anthropic.Anthropic(api_key=api_key) + + slug_dirs = sorted(p for p in SOLUTIONS.iterdir() if p.is_dir()) + if args.slug: + slug_dirs = [p for p in slug_dirs if p.name == args.slug] + + processed = 0 + for slug_dir in slug_dirs: + if args.limit and processed >= args.limit: + break + accepted_dir = slug_dir / "Accepted" + if not accepted_dir.is_dir(): + continue + # Pick the most recent accepted attempt that has a solution file. + accepted_code = None + target_dir = None + for ts_dir in sorted((p for p in accepted_dir.iterdir() if p.is_dir()), reverse=True): + code = read_solution(ts_dir) + if code: + accepted_code, target_dir = code, ts_dir + break + if not accepted_code or target_dir is None: + continue + out_path = target_dir / "EXPLANATION.md" + if out_path.exists() and not args.force: + continue + + attempts = collect_attempts(slug_dir) + prompt = build_prompt(slug_dir.name, accepted_code, attempts, args.tag) + + try: + # Adaptive thinking + streaming per Claude API guidance. + with client.messages.stream( + model=args.model, + max_tokens=2000, + system=SYSTEM_PROMPT, + thinking={"type": "adaptive"}, + messages=[{"role": "user", "content": prompt}], + ) as stream: + msg = stream.get_final_message() + except Exception as exc: # noqa: BLE001 - surface and continue + print(f"::warning::{slug_dir.name}: API error: {exc}", file=sys.stderr) + continue + + text = "".join(b.text for b in msg.content if b.type == "text").strip() + if not text: + print(f"::warning::{slug_dir.name}: empty response", file=sys.stderr) + continue + + header = ( + f"\n\n" + ) + out_path.write_text(header + text + "\n", encoding="utf-8") + print(f"wrote {out_path.relative_to(ROOT)}") + processed += 1 + + print(f"done: {processed} explanation(s) generated.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/compile_check.py b/scripts/compile_check.py new file mode 100644 index 0000000..926d09c --- /dev/null +++ b/scripts/compile_check.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python3 +"""Preamble-injection C++ syntax gate. + +The saved snippets are bare LeetCode editor bodies: a `class Solution { ... }` +(or a free function) with **no `#include` and no `using namespace std;`**. A +plain `g++ Solution.cpp` would fail to even parse `string`/`vector`/`reverse`, +so the repo's old CI "compiled" nothing and passed via `|| echo`. + +This gate fixes that. For each Accepted `Solution.cpp` it writes a temp file: + + #include + using namespace std; + + +then runs `g++ -std=c++20 -fsyntax-only -Wall` and fails on real compiler +errors (warnings do not fail the build). It is a *syntax* gate β€” it does not +link or run, because these are method bodies with no `main()` and no judge +harness. + +Only Accepted snippets are checked by default: Wrong-Answer / Runtime-Error / +TLE attempts are kept deliberately as part of the submission history and may +legitimately not even parse (Compile Error verdicts certainly won't). + +All filesystem access uses pathlib, so the spaces+commas in verdict/timestamp +directory names need no shell quoting here. (The README/CI shell one-liners that +glob these paths quote them; this script is the portable path.) + +Usage:: + + python scripts/compile_check.py # all Accepted .cpp + python scripts/compile_check.py --all-verdicts # every .cpp (expect failures) + python scripts/compile_check.py --cxx g++-13 --std c++20 +""" + +from __future__ import annotations + +import argparse +import shutil +import subprocess +import sys +import tempfile +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +SOLUTIONS = ROOT / "solutions" +PREAMBLE = "#include \nusing namespace std;\n" + + +def iter_cpp(all_verdicts: bool): + """Yield Solution.cpp paths. By default only those under an Accepted dir.""" + for slug_dir in sorted(p for p in SOLUTIONS.iterdir() if p.is_dir()): + for verdict_dir in sorted(p for p in slug_dir.iterdir() if p.is_dir()): + if not all_verdicts and verdict_dir.name != "Accepted": + continue + for sol in verdict_dir.rglob("Solution.cpp"): + yield sol + + +def check_one(cxx: str, std: str, src: Path) -> tuple[bool, str]: + body = src.read_text(encoding="utf-8", errors="replace") + with tempfile.NamedTemporaryFile( + "w", suffix=".cpp", delete=False, encoding="utf-8" + ) as tmp: + tmp.write(PREAMBLE) + tmp.write(body) + tmp_path = Path(tmp.name) + try: + proc = subprocess.run( + [cxx, f"-std={std}", "-fsyntax-only", "-Wall", str(tmp_path)], + capture_output=True, + text=True, + ) + return proc.returncode == 0, proc.stderr + finally: + tmp_path.unlink(missing_ok=True) + + +def main() -> int: + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument("--cxx", default="g++", help="C++ compiler (default: g++)") + ap.add_argument("--std", default="c++20", help="C++ standard (default: c++20)") + ap.add_argument( + "--all-verdicts", + action="store_true", + help="check every Solution.cpp, not just Accepted (expect failures)", + ) + args = ap.parse_args() + + if shutil.which(args.cxx) is None: + print( + f"::error::compiler '{args.cxx}' not found on PATH; cannot run the " + "C++ syntax gate.", + file=sys.stderr, + ) + return 2 + + failures: list[tuple[Path, str]] = [] + total = 0 + for src in iter_cpp(args.all_verdicts): + total += 1 + ok, stderr = check_one(args.cxx, args.std, src) + rel = src.relative_to(ROOT) + if ok: + print(f"PASS {rel}") + else: + print(f"FAIL {rel}") + failures.append((rel, stderr)) + + print(f"\nChecked {total} file(s); {len(failures)} failed.") + if failures: + print("\n--- failures ---", file=sys.stderr) + for rel, stderr in failures: + print(f"\n### {rel}", file=sys.stderr) + print(stderr.strip(), file=sys.stderr) + return 1 + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/generate_progress.py b/scripts/generate_progress.py new file mode 100644 index 0000000..dfc6e70 --- /dev/null +++ b/scripts/generate_progress.py @@ -0,0 +1,434 @@ +#!/usr/bin/env python3 +"""Single source of truth for this archive's statistics and docs. + +Walks ``solutions////Solution.``, reconciling +the two numbers the README used to contradict each other on: + +* **unique slugs** -- one directory per LeetCode problem +* **submission files** -- every saved attempt, across verdicts/timestamps + +It is the only place these counts are computed. From the parsed model it +regenerates: + +* ``docs/progress.json`` -- machine-readable stats (feeds dynamic badges) +* the ```` block in ``README.md`` +* ``docs/`` mkdocs-material pages (one per slug + indexes), when ``--docs`` + +Layout note: verdict and timestamp directory names contain spaces **and** +commas (e.g. ``Wrong Answer/8-8-2023, 12_20_23 AM``). Everything here uses +``pathlib`` so no shell quoting is involved; the CI globs that *do* touch the +shell quote every path (see ``.github/workflows``). + +Usage:: + + python scripts/generate_progress.py # regenerate stats + README + python scripts/generate_progress.py --docs # also (re)build docs/ pages + python scripts/generate_progress.py --check # exit 1 if anything is stale +""" + +from __future__ import annotations + +import argparse +import json +import re +import sys +from collections import Counter, defaultdict +from dataclasses import dataclass, field +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +SOLUTIONS = ROOT / "solutions" +DOCS = ROOT / "docs" +README = ROOT / "README.md" +PROGRESS_JSON = DOCS / "progress.json" + +# Maps file extension -> (display language, mkdocs content-tab fence language). +EXT_LANG = { + ".cpp": ("C++", "cpp"), + ".java": ("Java", "java"), + ".oraclesql": ("Oracle SQL", "sql"), + ".mysql": ("MySQL", "sql"), +} + +# First-line metadata hook present in every solution file, e.g. +# // https://leetcode.com/problems/two-sum +SLUG_RE = re.compile(r"leetcode\.com/problems/([a-z0-9-]+)", re.IGNORECASE) + +VERDICT_ORDER = [ + "Accepted", + "Wrong Answer", + "Time Limit Exceeded", + "Runtime Error", + "Compile Error", +] + +PROGRESS_START = "" +PROGRESS_END = "" + + +@dataclass +class Submission: + slug: str + verdict: str + timestamp: str + ext: str + path: Path + + @property + def language(self) -> str: + return EXT_LANG.get(self.ext, (self.ext.lstrip("."), "text"))[0] + + +@dataclass +class Problem: + slug: str + submissions: list[Submission] = field(default_factory=list) + + @property + def url(self) -> str: + return f"https://leetcode.com/problems/{self.slug}/" + + @property + def title(self) -> str: + return self.slug.replace("-", " ").title() + + @property + def accepted(self) -> bool: + return any(s.verdict == "Accepted" for s in self.submissions) + + @property + def languages(self) -> list[str]: + seen: list[str] = [] + for s in self.submissions: + if s.language not in seen: + seen.append(s.language) + return seen + + +def first_line_slug(path: Path) -> str | None: + """Return the slug from the file's first-line comment, if present.""" + try: + with path.open("r", encoding="utf-8", errors="replace") as fh: + for _ in range(5): # slug is on line 1, but tolerate a stray BOM/blank + line = fh.readline() + if not line: + break + m = SLUG_RE.search(line) + if m: + return m.group(1).lower() + except OSError: + return None + return None + + +def scan() -> dict[str, Problem]: + """Walk solutions/ and build the slug -> Problem map. + + Slug is taken from the first-line comment where available and falls back to + the directory name (they agree across the whole archive today, but the + comment is the authoritative metadata hook the README documents). + """ + problems: dict[str, Problem] = {} + if not SOLUTIONS.is_dir(): + return problems + + for slug_dir in sorted(p for p in SOLUTIONS.iterdir() if p.is_dir()): + dir_slug = slug_dir.name + for verdict_dir in sorted(p for p in slug_dir.iterdir() if p.is_dir()): + verdict = verdict_dir.name + for ts_dir in sorted(p for p in verdict_dir.iterdir() if p.is_dir()): + timestamp = ts_dir.name + for sol in sorted(ts_dir.iterdir()): + if not sol.is_file() or sol.suffix not in EXT_LANG: + continue + slug = first_line_slug(sol) or dir_slug + problems.setdefault(slug, Problem(slug)) + problems[slug].submissions.append( + Submission(slug, verdict, timestamp, sol.suffix, sol) + ) + return problems + + +def compute_stats(problems: dict[str, Problem]) -> dict: + lang_files: Counter[str] = Counter() + verdict_files: Counter[str] = Counter() + lang_slugs: dict[str, set[str]] = defaultdict(set) + total_files = 0 + accepted_slugs = 0 + + for slug, prob in problems.items(): + if prob.accepted: + accepted_slugs += 1 + for s in prob.submissions: + total_files += 1 + lang_files[s.language] += 1 + verdict_files[s.verdict] += 1 + lang_slugs[s.language].add(slug) + + unique_slugs = len(problems) + languages = [] + for lang in ("C++", "Java", "Oracle SQL", "MySQL"): + if lang_files.get(lang): + languages.append( + { + "language": lang, + "files": lang_files[lang], + "slugs": len(lang_slugs[lang]), + "pct_files": round(100 * lang_files[lang] / total_files, 1) + if total_files + else 0.0, + } + ) + + verdicts = [ + {"verdict": v, "files": verdict_files[v]} + for v in VERDICT_ORDER + if verdict_files.get(v) + ] + + accepted_pct = round(100 * accepted_slugs / unique_slugs, 1) if unique_slugs else 0.0 + + return { + "unique_slugs": unique_slugs, + "total_files": total_files, + "accepted_slugs": accepted_slugs, + "accepted_pct": accepted_pct, + "languages": languages, + "verdicts": verdicts, + } + + +def progress_json(stats: dict) -> dict: + """Shape for docs/progress.json -- also a shields.io endpoint badge source.""" + return { + "schemaVersion": 1, + "label": "solved", + "message": f"{stats['unique_slugs']} problems", + "color": "brightgreen", + "stats": stats, + } + + +def render_progress_block(stats: dict) -> str: + lines = [ + PROGRESS_START, + "", + f"- **Unique problems (slugs):** {stats['unique_slugs']}", + f"- **Total submission files:** {stats['total_files']} " + "(every saved attempt across verdicts and timestamps)", + f"- **Accepted problems:** {stats['accepted_slugs']} " + f"({stats['accepted_pct']}% of unique problems)", + "", + "### Language coverage", + "", + "| Language | Files | Problems | % of files |", + "|----------|-------|----------|-----------|", + ] + for lang in stats["languages"]: + lines.append( + f"| {lang['language']} | {lang['files']} | {lang['slugs']} | {lang['pct_files']}% |" + ) + lines.append( + f"| **Total** | **{stats['total_files']}** | **{stats['unique_slugs']}** | **100%** |" + ) + lines += ["", "### Submission verdicts", "", "| Verdict | Files |", "|---------|-------|"] + for v in stats["verdicts"]: + lines.append(f"| {v['verdict']} | {v['files']} |") + lines += [ + "", + f"*Generated by `scripts/generate_progress.py`. Do not edit this block by hand.*", + "", + PROGRESS_END, + ] + return "\n".join(lines) + + +def update_readme(stats: dict) -> str: + text = README.read_text(encoding="utf-8") + block = render_progress_block(stats) + if PROGRESS_START in text and PROGRESS_END in text: + new = re.sub( + re.escape(PROGRESS_START) + r".*?" + re.escape(PROGRESS_END), + block, + text, + flags=re.DOTALL, + ) + else: + # Insert the block right after the "## Progress Statistics" heading. + marker = "## πŸ“Š Progress Statistics" + if marker in text: + head, tail = text.split(marker, 1) + # Drop the old static table up to the next "##" heading. + rest = re.sub(r".*?(\n## )", r"\1", tail, count=1, flags=re.DOTALL) + new = f"{head}{marker}\n\n{block}\n{rest}" + else: + new = text + "\n\n## πŸ“Š Progress Statistics\n\n" + block + "\n" + return new + + +# --------------------------------------------------------------------------- # +# Docs generation (mkdocs-material) +# --------------------------------------------------------------------------- # + +def slugify_lang(lang: str) -> str: + return lang.lower().replace("+", "p").replace(" ", "-") + + +def build_docs(problems: dict[str, Problem], stats: dict) -> None: + pages = DOCS / "problems" + pages.mkdir(parents=True, exist_ok=True) + # Clear stale generated pages. + for old in pages.glob("*.md"): + old.unlink() + + for slug, prob in sorted(problems.items()): + write_problem_page(pages / f"{slug}.md", prob) + + write_problems_index(pages / "index.md", problems) + write_tags_index(DOCS / "tags.md", problems) + write_docs_home(DOCS / "index.md", stats, problems) + + +def write_problem_page(path: Path, prob: Problem) -> None: + lines = [ + f"# {prob.title}", + "", + f"[:material-open-in-new: Open on LeetCode]({prob.url}){{ .md-button }}", + "", + ] + # AI explanation, if the AI workflow produced one for any accepted attempt. + for s in prob.submissions: + expl = s.path.parent / "EXPLANATION.md" + if s.verdict == "Accepted" and expl.is_file(): + lines += ["## Explanation", "", expl.read_text(encoding="utf-8").strip(), ""] + break + + lines += ["## Verdict history", "", "| Verdict | Language | Submitted |", "|---------|----------|-----------|"] + for s in sorted(prob.submissions, key=lambda x: (VERDICT_ORDER.index(x.verdict) if x.verdict in VERDICT_ORDER else 99, x.timestamp)): + lines.append(f"| {s.verdict} | {s.language} | {s.timestamp} |") + lines.append("") + + # Group solutions into content tabs by language, accepted first. + lines += ["## Solutions", ""] + by_lang: dict[str, list[Submission]] = defaultdict(list) + for s in prob.submissions: + by_lang[s.language].append(s) + for lang, subs in by_lang.items(): + fence = EXT_LANG.get(subs[0].ext, ("", "text"))[1] + subs_sorted = sorted(subs, key=lambda x: (x.verdict != "Accepted", x.timestamp)) + lines.append(f'=== "{lang}"') + lines.append("") + for s in subs_sorted: + code = s.path.read_text(encoding="utf-8", errors="replace").rstrip() + lines.append(f" **{s.verdict}** β€” {s.timestamp}") + lines.append("") + lines.append(f" ```{fence}") + for codeline in code.splitlines(): + lines.append(f" {codeline}") + lines.append(" ```") + lines.append("") + path.write_text("\n".join(lines) + "\n", encoding="utf-8") + + +def write_problems_index(path: Path, problems: dict[str, Problem]) -> None: + lines = ["# All Problems", "", "| Problem | Status | Languages |", "|---------|--------|-----------|"] + for slug, prob in sorted(problems.items()): + status = "βœ… Accepted" if prob.accepted else "⏳ Attempted" + langs = ", ".join(prob.languages) + lines.append(f"| [{prob.title}]({slug}.md) | {status} | {langs} |") + lines.append("") + path.write_text("\n".join(lines) + "\n", encoding="utf-8") + + +def write_tags_index(path: Path, problems: dict[str, Problem]) -> None: + """Index by language; AI pattern/difficulty tags slot in here when present.""" + by_lang: dict[str, list[Problem]] = defaultdict(list) + for prob in problems.values(): + for lang in prob.languages: + by_lang[lang].append(prob) + lines = ["# Index by Language", ""] + for lang in sorted(by_lang): + lines.append(f"## {lang}") + lines.append("") + for prob in sorted(by_lang[lang], key=lambda p: p.slug): + lines.append(f"- [{prob.title}](problems/{prob.slug}.md)") + lines.append("") + path.write_text("\n".join(lines) + "\n", encoding="utf-8") + + +def write_docs_home(path: Path, stats: dict, problems: dict[str, Problem]) -> None: + lines = [ + "# LeetCode Solutions Archive", + "", + "A personal LeetCode archive with **real submission history** β€” including the", + "wrong-answer, runtime-error, and TLE attempts that came before each accepted", + "solution. Browse problems below or jump to the [index by language](tags.md).", + "", + "## At a glance", + "", + f"- **Unique problems:** {stats['unique_slugs']}", + f"- **Total submissions:** {stats['total_files']}", + f"- **Accepted:** {stats['accepted_slugs']} ({stats['accepted_pct']}%)", + "", + "| Language | Files | Problems |", + "|----------|-------|----------|", + ] + for lang in stats["languages"]: + lines.append(f"| {lang['language']} | {lang['files']} | {lang['slugs']} |") + lines += ["", "[Browse all problems](problems/index.md){ .md-button .md-button--primary }", ""] + path.write_text("\n".join(lines) + "\n", encoding="utf-8") + + +# --------------------------------------------------------------------------- # + + +def main() -> int: + ap = argparse.ArgumentParser(description=__doc__) + ap.add_argument("--docs", action="store_true", help="also (re)build docs/ pages") + ap.add_argument( + "--check", + action="store_true", + help="verify README/progress.json are up to date; exit 1 if stale", + ) + args = ap.parse_args() + + problems = scan() + stats = compute_stats(problems) + pj = progress_json(stats) + new_readme = update_readme(stats) + pj_text = json.dumps(pj, indent=2) + "\n" + + print( + f"slugs={stats['unique_slugs']} files={stats['total_files']} " + f"accepted={stats['accepted_slugs']} ({stats['accepted_pct']}%)" + ) + for lang in stats["languages"]: + print(f" {lang['language']:<12} files={lang['files']:<4} slugs={lang['slugs']}") + + if args.check: + stale = [] + current_readme = README.read_text(encoding="utf-8") + if current_readme != new_readme: + stale.append("README.md progress block") + if not PROGRESS_JSON.is_file() or PROGRESS_JSON.read_text(encoding="utf-8") != pj_text: + stale.append("docs/progress.json") + if stale: + print("STALE (run scripts/generate_progress.py): " + ", ".join(stale), file=sys.stderr) + return 1 + print("OK: generated artifacts are up to date.") + return 0 + + DOCS.mkdir(parents=True, exist_ok=True) + PROGRESS_JSON.write_text(pj_text, encoding="utf-8") + README.write_text(new_readme, encoding="utf-8") + print(f"wrote {PROGRESS_JSON.relative_to(ROOT)} and updated README.md") + + if args.docs: + build_docs(problems, stats) + print(f"wrote docs/ pages for {len(problems)} problems") + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 301791817f48c778c4f8286479ccb6eaf33aa931 Mon Sep 17 00:00:00 2001 From: Ali Ammari Date: Fri, 12 Jun 2026 02:12:25 +0100 Subject: [PATCH 3/9] feat: add C++/SQL/docs tooling configs .clang-format (Google-based, 4-space) for the bare C++ snippets; .sqlfluff (dialect=oracle default, lenient lint that ignores the // comment line); mkdocs.yml (material, dark pattern theme, content tabs, language tabs); pinned requirements-docs.txt (mkdocs-material in maintenance mode -> pin a known-good version); .gitattributes to keep LF on scripts/configs. Co-Authored-By: Claude Opus 4.8 (1M context) --- .clang-format | 12 ++++++++ .gitattributes | 5 ++++ .sqlfluff | 20 +++++++++++++ mkdocs.yml | 69 +++++++++++++++++++++++++++++++++++++++++++ requirements-docs.txt | 5 ++++ 5 files changed, 111 insertions(+) create mode 100644 .clang-format create mode 100644 .gitattributes create mode 100644 .sqlfluff create mode 100644 mkdocs.yml create mode 100644 requirements-docs.txt diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..0091edf --- /dev/null +++ b/.clang-format @@ -0,0 +1,12 @@ +# clang-format config for the C++ solution snippets. +# These are bare LeetCode editor bodies (no includes), so formatting is the only +# whole-file check that's meaningful; the syntax gate handles correctness. +BasedOnStyle: Google +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +ColumnLimit: 100 +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +SortIncludes: false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..93f5a46 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Keep LF for scripts and config so CI bash/Python behave identically on all OSes. +* text=auto eol=lf +*.png binary +*.jpg binary +*.svg text eol=lf diff --git a/.sqlfluff b/.sqlfluff new file mode 100644 index 0000000..48eb5f2 --- /dev/null +++ b/.sqlfluff @@ -0,0 +1,20 @@ +# sqlfluff config. The default dialect is oracle (most DB solutions are +# .oraclesql); the MySQL files are linted with `--dialect mysql` overriding this +# in the dedicated CI step. Lint-only β€” these are LeetCode editor snippets, not +# a managed schema, so we keep the rule set lenient and skip auto-fix in CI. +[sqlfluff] +dialect = oracle +templater = raw +max_line_length = 120 +# The files open with a `// https://...` comment line that is not valid SQL in +# either dialect; ignore parsing/templating noise from that and focus on style. +ignore = parsing,templating + +[sqlfluff:indentation] +tab_space_size = 4 + +[sqlfluff:rules:capitalisation.keywords] +capitalisation_policy = consistent + +[sqlfluff:rules:capitalisation.identifiers] +extended_capitalisation_policy = consistent diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..8f85a70 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,69 @@ +site_name: LeetCode Solutions Archive +site_description: >- + A personal LeetCode archive with real submission history β€” including the wrong + attempts before each accepted solution β€” across C++, Java, and Oracle/MySQL SQL. +site_author: Ali Ammari +repo_url: https://github.com/aliammari1/Leetcode_problems +repo_name: aliammari1/Leetcode_problems +docs_dir: docs +copyright: >- + Solution code Β© Ali Ammari (MIT). LeetCode problem statements and trademarks + are the property of LeetCode. + +theme: + name: material + palette: + - scheme: slate + primary: black + accent: amber + toggle: + icon: material/weather-sunny + name: Switch to light mode + - scheme: default + primary: black + accent: amber + toggle: + icon: material/weather-night + name: Switch to dark mode + features: + - content.code.copy + - content.tabs.link + - navigation.tabs + - navigation.top + - navigation.instant + - search.highlight + - search.suggest + icon: + repo: fontawesome/brands/github + +markdown_extensions: + - admonition + - attr_list + - md_in_html + - pymdownx.details + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true + - toc: + permalink: true + +plugins: + - search + # Note: the per-slug pages under docs/problems/ are GENERATED fresh by + # scripts/generate_progress.py --docs and are not committed, so a + # git-revision-date plugin would warn on every page ("no git logs") and fail + # --strict. We deliberately omit it; the build date is shown by the theme. + +nav: + - Home: index.md + - All Problems: problems/index.md + - Index by Language: tags.md + +extra: + social: + - icon: fontawesome/brands/github + link: https://github.com/aliammari1 + - icon: simple/leetcode + link: https://leetcode.com/aliammari1/ diff --git a/requirements-docs.txt b/requirements-docs.txt new file mode 100644 index 0000000..db96278 --- /dev/null +++ b/requirements-docs.txt @@ -0,0 +1,5 @@ +# Pinned docs toolchain. mkdocs-material entered maintenance mode in late 2025; +# pin a known-good release rather than tracking latest. +mkdocs-material==9.5.49 +mkdocs==1.6.1 +pymdown-extensions==10.14.3 From 74629c87b6c5043c37de1d90217ec02050114fbe Mon Sep 17 00:00:00 2001 From: Ali Ammari Date: Fri, 12 Jun 2026 02:12:41 +0100 Subject: [PATCH 4/9] ci: real compile/lint gates replacing the vacuous g++ CI The saved snippets have no #include, so the old `g++ ... || echo` CI compiled nothing and always passed. Replace with real jobs that fail on error: - cpp-syntax: preamble-injection gate (inject bits/stdc++.h + using namespace std into a temp file, then g++ -std=c++20 -fsyntax-only -Wall). - clang-format + cpp-linter (format-only; no compile DB for bare snippets). - sqlfluff: --dialect oracle for .oraclesql, --dialect mysql for .mysql. - progress: generate_progress.py --check (stats must match solutions/). - docs: mkdocs build --strict. All file globbing is NUL-delimited and quoted (verdict/timestamp dirs contain spaces and commas). Verified locally: sqlfluff passes on all 26 Oracle + 2 MySQL files; generator --check and mkdocs --strict both pass. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ci.yml | 109 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..13cda78 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,109 @@ +name: CI + +on: + push: + branches: [main, master] + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + progress: + name: Progress table is current + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + # Fails if README's PROGRESS block or docs/progress.json drifted from + # solutions/ β€” keeps the 173-vs-542 stats honest and reconciled. + - name: Check generated stats + run: python scripts/generate_progress.py --check + + cpp-syntax: + name: C++ preamble-injection syntax gate + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install g++ + run: sudo apt-get update && sudo apt-get install -y g++ + # Injects `#include ` + `using namespace std;` then runs + # g++ -fsyntax-only. Real errors fail the build (no `|| echo`). + - name: Syntax-check all Accepted C++ solutions + run: python scripts/compile_check.py --cxx g++ --std c++20 + + clang-format: + name: clang-format + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: clang-format check (quoted globs) + run: | + # Paths contain spaces and commas β€” NUL-delimit and quote. + mapfile -d '' files < <(find solutions -type f -name 'Solution.cpp' -print0) + if [ ${#files[@]} -eq 0 ]; then echo "no C++ files"; exit 0; fi + clang-format --dry-run --Werror "${files[@]}" + + cpp-linter: + name: cpp-linter + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cpp-linter/cpp-linter-action@v2 + id: linter + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + style: file + tidy-checks: "-*" # format-only; bare snippets have no compile DB + files-changed-only: false + extensions: cpp + - name: Fail on lint findings + if: steps.linter.outputs.checks-failed > 0 + run: exit 1 + + sql: + name: sqlfluff (Oracle + MySQL) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install sqlfluff + run: pip install "sqlfluff==3.3.0" + - name: Lint Oracle SQL (quoted globs) + run: | + mapfile -d '' files < <(find solutions -type f -name '*.oraclesql' -print0) + if [ ${#files[@]} -eq 0 ]; then echo "no Oracle SQL"; exit 0; fi + sqlfluff lint --dialect oracle "${files[@]}" + - name: Lint MySQL (quoted globs) + run: | + mapfile -d '' files < <(find solutions -type f -name '*.mysql' -print0) + if [ ${#files[@]} -eq 0 ]; then echo "no MySQL"; exit 0; fi + sqlfluff lint --dialect mysql "${files[@]}" + + docs: + name: mkdocs build (strict) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install docs deps + run: pip install -r requirements-docs.txt + - name: Generate pages + run: python scripts/generate_progress.py --docs + - name: Build + run: mkdocs build --strict From 2cb33624cedf07e7a4dba561e1f685349f031abc Mon Sep 17 00:00:00 2001 From: Ali Ammari Date: Fri, 12 Jun 2026 02:12:55 +0100 Subject: [PATCH 5/9] feat: gated docs deploy + AI explanation/review workflows - docs.yml: build mkdocs and deploy to Cloudflare Pages via wrangler-action, gated on CLOUDFLARE_API_TOKEN/ACCOUNT_ID (build-only when secrets absent; no CF account needed during development). - ai-explain.yml + scripts/ai_explain.py: for each Accepted solution, Claude (claude-sonnet-4-6, configurable) writes EXPLANATION.md covering approach, complexity, and a diff vs the sibling Wrong-Answer/Runtime-Error attempts -- the unique hook. No-op when ANTHROPIC_API_KEY is unset. Output folds into the mkdocs problem pages and can auto-tag difficulty/pattern. - claude-review.yml: claude-code-action PR review, gated on the API key. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/ai-explain.yml | 62 +++++++++++++++++++++++++++++ .github/workflows/claude-review.yml | 29 ++++++++++++++ .github/workflows/docs.yml | 56 ++++++++++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 .github/workflows/ai-explain.yml create mode 100644 .github/workflows/claude-review.yml create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/ai-explain.yml b/.github/workflows/ai-explain.yml new file mode 100644 index 0000000..fb424b6 --- /dev/null +++ b/.github/workflows/ai-explain.yml @@ -0,0 +1,62 @@ +name: AI solution explanations + +on: + workflow_dispatch: + inputs: + limit: + description: "Max number of slugs to process (0 = all)" + default: "20" + force: + description: "Overwrite existing EXPLANATION.md" + type: boolean + default: false + schedule: + # Weekly top-up of any newly-added Accepted solutions lacking an explanation. + - cron: "0 5 * * 1" + +permissions: + contents: write + pull-requests: write + +jobs: + explain: + name: Generate EXPLANATION.md with Claude + runs-on: ubuntu-latest + # Gated: the job only runs when ANTHROPIC_API_KEY is configured. + if: ${{ vars.ENABLE_AI == 'true' || github.event_name == 'workflow_dispatch' }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install Anthropic SDK + run: pip install "anthropic>=0.40" + + - name: Generate explanations (with WA/RE diff hook) + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + AI_MODEL: ${{ vars.AI_MODEL || 'claude-sonnet-4-6' }} + run: | + # Script is a no-op when ANTHROPIC_API_KEY is unset, so this is safe + # to run on PRs / forks without the secret. + ARGS="--tag --limit ${{ github.event.inputs.limit || '20' }}" + if [ "${{ github.event.inputs.force }}" = "true" ]; then ARGS="$ARGS --force"; fi + python scripts/ai_explain.py $ARGS + + - name: Open PR with new explanations + if: ${{ env.HAS_KEY == 'true' }} + env: + HAS_KEY: ${{ secrets.ANTHROPIC_API_KEY != '' }} + uses: peter-evans/create-pull-request@v6 + with: + commit-message: "docs: add AI-generated solution explanations" + title: "docs: AI solution explanations" + body: | + Auto-generated `EXPLANATION.md` files (approach, complexity, and a + diff vs the recorded Wrong-Answer / Runtime-Error attempts). + Review before merging. + branch: ai/explanations + add-paths: | + solutions/**/EXPLANATION.md diff --git a/.github/workflows/claude-review.yml b/.github/workflows/claude-review.yml new file mode 100644 index 0000000..6bba6b5 --- /dev/null +++ b/.github/workflows/claude-review.yml @@ -0,0 +1,29 @@ +name: Claude PR review + +on: + pull_request: + types: [opened, synchronize] + issue_comment: + types: [created] + +permissions: + contents: read + pull-requests: write + issues: write + +jobs: + review: + name: "@claude review" + runs-on: ubuntu-latest + # Gated on the secret: skips cleanly when ANTHROPIC_API_KEY is absent. + if: ${{ github.event_name == 'pull_request' || contains(github.event.comment.body, '@claude') }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Claude Code Action + uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + # Configurable; defaults to Sonnet for cost on an archive repo. + model: ${{ vars.AI_MODEL || 'claude-sonnet-4-6' }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..9eddf48 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,56 @@ +name: Deploy docs (Cloudflare Pages) + +on: + push: + branches: [main, master] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: docs-deploy + cancel-in-progress: true + +jobs: + deploy: + name: Build & deploy to Cloudflare Pages + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install docs deps + run: pip install -r requirements-docs.txt + + - name: Generate per-slug pages + run: python scripts/generate_progress.py --docs + + - name: Build site + run: mkdocs build --strict + + # Gated: only deploys when the Cloudflare secrets are configured. On forks + # / PRs without them this whole step is skipped, so the workflow stays + # green without ever needing a CF account during development. + - name: Check Cloudflare secrets + id: cf + run: | + if [ -n "${{ secrets.CLOUDFLARE_API_TOKEN }}" ] && [ -n "${{ secrets.CLOUDFLARE_ACCOUNT_ID }}" ]; then + echo "enabled=true" >> "$GITHUB_OUTPUT" + else + echo "enabled=false" >> "$GITHUB_OUTPUT" + echo "::notice::Cloudflare secrets not set β€” skipping deploy (build verified only)." + fi + + - name: Deploy to Cloudflare Pages + if: steps.cf.outputs.enabled == 'true' + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy site --project-name=leetcode-problems From 3b124a468a79f704d9e61c7aa3517ba00fdc8cd3 Mon Sep 17 00:00:00 2001 From: Ali Ammari Date: Fri, 12 Jun 2026 02:13:21 +0100 Subject: [PATCH 6/9] chore: community health, dependency bots, pre-commit, banner brief Dependabot (github-actions) + Renovate (one config each; pick one when enabling), pre-commit mirroring the CI gates, CodeRabbit config (skips historical snippets), SECURITY.md (tooling-scoped), a solution issue template, and BANNER.md describing the pattern-grid dark direction (assets/ banner is a TODO, referenced in README). Co-Authored-By: Claude Opus 4.8 (1M context) --- .coderabbit.yaml | 15 +++++++++++ .github/ISSUE_TEMPLATE/solution_issue.md | 15 +++++++++++ .github/dependabot.yml | 10 +++++++ .pre-commit-config.yaml | 33 ++++++++++++++++++++++++ BANNER.md | 28 ++++++++++++++++++++ SECURITY.md | 19 ++++++++++++++ renovate.json | 15 +++++++++++ 7 files changed, 135 insertions(+) create mode 100644 .coderabbit.yaml create mode 100644 .github/ISSUE_TEMPLATE/solution_issue.md create mode 100644 .github/dependabot.yml create mode 100644 .pre-commit-config.yaml create mode 100644 BANNER.md create mode 100644 SECURITY.md create mode 100644 renovate.json diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..98d08e6 --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,15 @@ +# CodeRabbit config β€” lightweight, archive-appropriate. +language: en-US +reviews: + profile: chill + request_changes_workflow: false + high_level_summary: true + poem: false + review_status: true + path_filters: + - "!solutions/**" # don't nitpick historical submission snippets + - "scripts/**" + - ".github/**" + - "*.md" +chat: + auto_reply: true diff --git a/.github/ISSUE_TEMPLATE/solution_issue.md b/.github/ISSUE_TEMPLATE/solution_issue.md new file mode 100644 index 0000000..39631a9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/solution_issue.md @@ -0,0 +1,15 @@ +--- +name: Solution issue +about: Report an incorrect solution or suggest an improvement +title: "[slug] short description" +labels: ["solution"] +--- + +**Problem slug:** + +**Language:** + +**What's wrong / what could be better?** + + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..85005ba --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +# Archive repo: GitHub Actions are the only moving dependency worth tracking. +# (Renovate config also present for those who prefer it; pick one when enabling.) +updates: + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + commit-message: + prefix: "ci" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..15ba879 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,33 @@ +# Mirrors the CI gates locally. Install: pip install pre-commit && pre-commit install +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: end-of-file-fixer + exclude: '^solutions/' + - id: trailing-whitespace + exclude: '^solutions/' + - id: check-yaml + + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v19.1.5 + hooks: + - id: clang-format + files: '^solutions/.*Solution\.cpp$' + + - repo: https://github.com/sqlfluff/sqlfluff + rev: 3.3.0 + hooks: + - id: sqlfluff-lint + name: sqlfluff (oracle) + files: '\.oraclesql$' + args: ["--dialect", "oracle"] + + - repo: local + hooks: + - id: progress-table + name: progress table up to date + entry: python scripts/generate_progress.py --check + language: system + pass_filenames: false + files: '^solutions/|^scripts/generate_progress\.py$' diff --git a/BANNER.md b/BANNER.md new file mode 100644 index 0000000..f6b1fb7 --- /dev/null +++ b/BANNER.md @@ -0,0 +1,28 @@ +# Banner / social preview β€” TODO + +The repository banner and GitHub social-preview image are **not yet generated**. +This file is the brief for producing them. + +## Direction + +**Pattern-grid, dark.** A dense grid evoking a LeetCode problem matrix / +submission heatmap β€” small monochrome tiles on a near-black background, a few +accent tiles in amber/green marking "Accepted", subtle muted reds/oranges for +the Wrong-Answer / Runtime-Error / TLE attempts that this archive uniquely +preserves. Mono/technical typeface. The hook to convey: *real submission +history, including the wrong attempts*. + +## Deliverables + +- `assets/banner.png` β€” wide README hero (referenced as a TODO comment at the + top of `README.md`). +- `assets/social-preview.png` β€” **1280Γ—640**, set via GitHub β†’ + Settings β†’ Social preview. + +## How to generate + +Use the `brandkit` Claude skill for the identity board + 1280Γ—640 social card, +and `imagegen-frontend-web` for the wide README hero. Commit the results as +local PNG/SVG under `assets/` (never hot-link, to avoid 404 / rate-limit). + +Once committed, uncomment the banner line at the top of `README.md`. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..085cfdf --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,19 @@ +# Security Policy + +This repository is an archive of personal LeetCode solutions β€” it ships no +running service, no dependencies that execute at runtime, and stores no secrets. +The practical security surface is the CI/automation tooling. + +## Reporting + +If you find a problem in the tooling (a workflow that could leak a token, a +script with a command-injection issue, etc.), please open a +[GitHub Security Advisory](https://github.com/aliammari1/Leetcode_problems/security/advisories/new) +or email the maintainer. Please do **not** open a public issue for a +security-relevant report. + +## Scope + +- In scope: `scripts/`, `.github/workflows/`, CI configuration. +- Out of scope: the correctness of individual solution snippets (those are a + historical record, including deliberately-kept failed attempts). diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..8bf99af --- /dev/null +++ b/renovate.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended", ":semanticCommits"], + "labels": ["dependencies"], + "enabledManagers": ["github-actions", "pip_requirements"], + "github-actions": { + "pinDigests": true + }, + "packageRules": [ + { + "matchManagers": ["github-actions"], + "groupName": "github-actions" + } + ] +} From 34148b5cd98c731ad048c545988c8f1c3f526fa3 Mon Sep 17 00:00:00 2001 From: Ali Ammari Date: Fri, 12 Jun 2026 12:17:57 +0100 Subject: [PATCH 7/9] =?UTF-8?q?docs:=20trending=20polish=20=E2=80=94=20pat?= =?UTF-8?q?tern=20index,=20live=20demo=20URL,=20banner=20prompt,=20FUNDING?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README: add 14-pattern index (all slugs verified to exist), SQL-50/Oracle track section, live Cloudflare Pages demo URL + deploy steps, sharper docs showcase copy. - BANNER.md: finalize into one verbatim image-gen prompt (dark pattern grid, 14 pattern tiles, C++/Java/Oracle marks, "173 problems Β· 542 submissions", 1280x640 + hero); README references assets/banner.png. - Add .github/FUNDING.yml (github: aliammari1). - Remove duplicate renovate.json; fold pip ecosystem into Dependabot so a single dependency bot covers github-actions + docs deps (no duplicate update PRs). Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/FUNDING.yml | 1 + .github/dependabot.yml | 17 +++++++- BANNER.md | 62 ++++++++++++++++++---------- README.md | 91 ++++++++++++++++++++++++++++-------------- renovate.json | 15 ------- 5 files changed, 117 insertions(+), 69 deletions(-) create mode 100644 .github/FUNDING.yml delete mode 100644 renovate.json diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..6698e40 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [aliammari1] diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 85005ba..c7b7067 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,6 +1,7 @@ version: 2 -# Archive repo: GitHub Actions are the only moving dependency worth tracking. -# (Renovate config also present for those who prefer it; pick one when enabling.) +# Archive repo: the only moving dependencies are GitHub Actions and the docs +# build tooling (requirements-docs.txt). Dependabot is the single dependency +# bot here β€” a Renovate config was removed to avoid duplicate update PRs. updates: - package-ecosystem: github-actions directory: "/" @@ -8,3 +9,15 @@ updates: interval: weekly commit-message: prefix: "ci" + labels: ["dependencies"] + groups: + github-actions: + patterns: ["*"] + + - package-ecosystem: pip + directory: "/" + schedule: + interval: weekly + commit-message: + prefix: "build" + labels: ["dependencies"] diff --git a/BANNER.md b/BANNER.md index f6b1fb7..38b882f 100644 --- a/BANNER.md +++ b/BANNER.md @@ -1,28 +1,48 @@ -# Banner / social preview β€” TODO +# Banner / social preview -The repository banner and GitHub social-preview image are **not yet generated**. -This file is the brief for producing them. - -## Direction - -**Pattern-grid, dark.** A dense grid evoking a LeetCode problem matrix / -submission heatmap β€” small monochrome tiles on a near-black background, a few -accent tiles in amber/green marking "Accepted", subtle muted reds/oranges for -the Wrong-Answer / Runtime-Error / TLE attempts that this archive uniquely -preserves. Mono/technical typeface. The hook to convey: *real submission -history, including the wrong attempts*. +The repository banner and GitHub social-preview image are generated from the +**single image-generation prompt** below. Run it, then commit the outputs under +`assets/` and uncomment the banner line at the top of `README.md`. ## Deliverables -- `assets/banner.png` β€” wide README hero (referenced as a TODO comment at the - top of `README.md`). -- `assets/social-preview.png` β€” **1280Γ—640**, set via GitHub β†’ - Settings β†’ Social preview. +- `assets/banner.png` β€” wide README hero (referenced by the commented-out line + at the top of `README.md` as `assets/banner.png`). +- `assets/social-preview.png` β€” **1280Γ—640**, set via GitHub β†’ Settings β†’ + Social preview. + +Commit the results as local PNG/SVG under `assets/` (never hot-link, to avoid +404 / rate-limit). Keep the same composition for both sizes so they read as one +identity. + +## The prompt (use verbatim) + +> A dark, near-black technical banner for a LeetCode solution archive. The +> background is a dense **pattern grid / submission heatmap**: small square +> monochrome tiles in a tight matrix. Most tiles are muted dark-grey; +> **accent tiles glow amber and green for "Accepted"**, with a scattering of +> **muted red / orange tiles marking the Wrong-Answer, Runtime-Error, and +> Time-Limit-Exceeded attempts** β€” conveying the hook *"real submission +> history, including the wrong attempts."* +> +> Overlaid on the grid, **14 labelled pattern tiles** in a clean mono/technical +> typeface, one per algorithm family: **Arrays, Two Pointers, Sliding Window, +> Binary Search, Hashing, Stack, Linked List, Trees, BFS/DFS, Divide & +> Conquer, Greedy, Dynamic Programming, Math, SQL**. +> +> A compact language strip with three marks: **C++**, **Java**, **Oracle SQL**. +> +> Bold headline text: **"173 problems Β· 542 submissions"**. Small subtitle: +> *"every accepted solution β€” and the wrong attempts before it."* +> +> Aesthetic: editorial dark-tech, high contrast, generous spacing, amber accent +> (#FFB300-ish) on near-black (#0B0B0D), no clutter, no photographic elements, +> crisp pixel-grid feel. +> +> Render two crops sharing this exact composition: a **1280Γ—640** social card +> (`social-preview.png`) and a **wide README hero** banner (`banner.png`). ## How to generate -Use the `brandkit` Claude skill for the identity board + 1280Γ—640 social card, -and `imagegen-frontend-web` for the wide README hero. Commit the results as -local PNG/SVG under `assets/` (never hot-link, to avoid 404 / rate-limit). - -Once committed, uncomment the banner line at the top of `README.md`. +Use the `brandkit` Claude skill for the 1280Γ—640 social card and +`imagegen-frontend-web` for the wide README hero, feeding both the prompt above. diff --git a/README.md b/README.md index 4b625be..0f147b6 100644 --- a/README.md +++ b/README.md @@ -111,28 +111,41 @@ Each problem follows this consistent structure: - **Timestamp Directories**: Each submission saved with exact timestamp - **Solution Files**: Named by language (e.g., `Solution.cpp`, `Solution.java`, `Solution.oraclesql`) -## 🎯 Problem Categories - -This repository covers a wide range of topics: - -### Data Structures -- **Arrays**: Two Sum, Best Time to Buy and Sell Stock, Build Array from Permutation -- **Strings**: Valid Anagram, Longest Common Prefix, Reverse String -- **Linked Lists**: Reverse Linked List, Merge Two Sorted Lists, Middle of the Linked List -- **Trees**: Binary Tree Traversals, Maximum Depth of Binary Tree, Same Tree -- **Hash Tables**: Contains Duplicate, Single Number, Valid Anagram -- **Stacks**: Valid Parentheses, Min Stack, Baseball Game - -### Algorithms -- **Math Problems**: Fibonacci Number, Power calculations, Palindrome Number -- **Binary Search**: Binary Search, First Bad Version, Search Insert Position -- **Two Pointers**: Two Sum II, Reverse String, Valid Palindrome -- **Simulation**: Fizz Buzz, Robot Return to Origin, Pascal's Triangle - -### Database Problems (SQL) -- **Joins**: Combine Two Tables, Replace Employee ID -- **Aggregation**: Average Salary, Daily Leads and Partners -- **Filtering**: Big Countries, Not Boring Movies, Recyclable Products +## 🧭 Pattern index + +The full, auto-generated index by language/pattern lives on the docs site +([Index by Language](https://leetcode-problems.pages.dev/tags/)). The 14 core +patterns this archive covers: + +| Pattern | Example problems (slug) | +|---------|-------------------------| +| Arrays | `two-sum`, `best-time-to-buy-and-sell-stock`, `build-array-from-permutation` | +| Two Pointers | `two-sum-ii-input-array-is-sorted`, `palindrome-linked-list`, `backspace-string-compare` | +| Sliding Window | `best-time-to-buy-and-sell-stock`, `max-consecutive-ones` | +| Binary Search | `binary-search`, `search-insert-position`, `first-bad-version` | +| Hashing | `contains-duplicate`, `valid-anagram`, `single-number` | +| Stack | `valid-parentheses`, `baseball-game`, `min-stack` | +| Linked List | `reverse-linked-list`, `merge-two-sorted-lists`, `middle-of-the-linked-list` | +| Trees | `binary-tree-inorder-traversal`, `symmetric-tree`, `validate-binary-search-tree` | +| BFS / DFS | `binary-tree-level-order-traversal`, `binary-tree-right-side-view` | +| Divide & Conquer | `median-of-two-sorted-arrays`, `merge-two-sorted-lists` | +| Greedy | `can-place-flowers`, `can-make-arithmetic-progression-from-sequence` | +| Dynamic Programming | `climbing-stairs`, `fibonacci-number` | +| Math | `palindrome-number`, `convert-the-temperature`, `add-binary` | +| SQL | `combine-two-tables`, `big-countries`, `article-views-i` | + +### πŸ—„οΈ SQL-50 / Oracle track + +Database solutions follow LeetCode's **SQL-50** study plan. A quirk worth +calling out: SQL-50 is **MySQL-graded**, but most queries here were authored and +saved as **Oracle PL/SQL** (`.oraclesql`, 26 files / 22 problems) with a couple +of MySQL files (`.mysql`). They are linted accordingly β€” +`sqlfluff --dialect oracle` for the Oracle files and `--dialect mysql` for the +MySQL ones (see [`.sqlfluff`](.sqlfluff)). Representative problems: joins +(`combine-two-tables`, `replace-employee-id-with-the-unique-identifier`), +aggregation (`average-salary-excluding-the-minimum-and-maximum-salary`, +`daily-leads-and-partners`), filtering (`big-countries`, `not-boring-movies`, +`recyclable-and-low-fat-products`). ## πŸ” Example Solutions @@ -257,22 +270,38 @@ clang-format --dry-run --Werror $(git ls-files 'solutions/**/Solution.cpp') sqlfluff lint --dialect oracle $(git ls-files 'solutions/**/*.oraclesql') ``` -## πŸ“– Documentation site +## πŸ“– Documentation site (live demo) + +> **Live demo:** https://leetcode-problems.pages.dev An [mkdocs-material](https://squidfunk.github.io/mkdocs-material/) site is -generated from `solutions/` β€” one page per problem with its **verdict history** -and every language variant in content tabs, plus an index by language. Build it: +generated from `solutions/` β€” **one page per problem slug** showing its full +**verdict history** (Accepted β†’ Wrong Answer β†’ Runtime Error β†’ TLE, each +timestamped) with every language variant in content tabs, plus an index by +language. This is the showcase for the archive's unique angle: you can read the +*progression* of attempts, not just the final answer. + +Build / preview it locally: ```bash pip install -r requirements-docs.txt -python scripts/generate_progress.py --docs -mkdocs build --strict -mkdocs serve # local preview +python scripts/generate_progress.py --docs # writes docs/problems/*.md, docs/index.md, docs/tags.md +mkdocs build --strict # verifies all 173 pages +mkdocs serve # http://127.0.0.1:8000 ``` -The site deploys to **Cloudflare Pages** via a gated workflow (see -`.github/workflows/docs.yml`); the deploy step only runs when the -`CLOUDFLARE_API_TOKEN` / `CLOUDFLARE_ACCOUNT_ID` secrets are present. +**Deploy (Cloudflare Pages).** Push to `main` triggers +`.github/workflows/docs.yml`, which regenerates the per-slug pages, runs +`mkdocs build --strict`, and deploys `site/` to a Cloudflare Pages project named +`leetcode-problems`. The deploy step is **gated** β€” it only runs when the +`CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` repository secrets are set, so +forks and PRs still get a green build without any Cloudflare account. To wire up +your own: + +1. Create a Cloudflare Pages project (Direct Upload) named `leetcode-problems`. +2. Add `CLOUDFLARE_API_TOKEN` (Pages:Edit) and `CLOUDFLARE_ACCOUNT_ID` as repo + secrets. +3. Push to `main` β€” the workflow builds and deploys automatically. ## πŸ€– AI tooling diff --git a/renovate.json b/renovate.json deleted file mode 100644 index 8bf99af..0000000 --- a/renovate.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": ["config:recommended", ":semanticCommits"], - "labels": ["dependencies"], - "enabledManagers": ["github-actions", "pip_requirements"], - "github-actions": { - "pinDigests": true - }, - "packageRules": [ - { - "matchManagers": ["github-actions"], - "groupName": "github-actions" - } - ] -} From 8f55e4cadfec4af0f70be98092f3be7a4110023c Mon Sep 17 00:00:00 2001 From: Ali Ammari Date: Fri, 12 Jun 2026 14:40:37 +0100 Subject: [PATCH 8/9] docs: star-growth hook, SEO page titles, lessons index, growth kit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README H1 is now the killer hook ("every problem I solved β€” including my real Wrong-Answer / TLE submissions and the lesson from each"); add a searchable "Browse all 173 problems" top button, a star CTA, and the leetcode-solutions MCP (Wave 2) teaser. Keep accurate 173/542 stats. - SEO: per-slug docs page titles are now exact "<#>. β€” Solution" via a new scripts/problem_numbers.json slug->number map (graceful fallback when a number is unknown). - New docs/lessons.md "Lessons / common mistakes" index (89 problems with real failed attempts, 265 preserved) + nav entry; docs home surfaces it and the searchable browse button. - Cross-link footer (Related: HackerRank archive + profile hub + LeetCode) on every problem page, the docs home, and the README. - Add GROWTH.md: About, 8 topics (leetcode, leetcode-solutions, algorithms, sql, oracle-sql, interview-prep, coding-interview, data-structures), the r/leetcode "public WITH all my failed submissions" post, awesome-list submission lines, and the MCP angle. Verified: mkdocs build --strict (clean, 173 pages) and generate_progress.py --check both pass. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 1 + GROWTH.md | 119 +++++++++++++++++++++++ README.md | 36 +++++-- mkdocs.yml | 1 + scripts/generate_progress.py | 123 ++++++++++++++++++++++-- scripts/problem_numbers.json | 176 +++++++++++++++++++++++++++++++++++ 6 files changed, 443 insertions(+), 13 deletions(-) create mode 100644 GROWTH.md create mode 100644 scripts/problem_numbers.json diff --git a/.gitignore b/.gitignore index e7a15ce..cc0b353 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ /docs/problems/ /docs/index.md /docs/tags.md +/docs/lessons.md # Python __pycache__/ diff --git a/GROWTH.md b/GROWTH.md new file mode 100644 index 0000000..7fcfcd6 --- /dev/null +++ b/GROWTH.md @@ -0,0 +1,119 @@ +# GROWTH.md β€” distribution kit for `Leetcode_problems` + +Internal, not part of the published docs. Everything here needs **your GitHub / +Reddit account** to execute. The repo content (README hook, mkdocs site, lessons +index, SEO titles, cross-links) is already done on `feat/upgrade`. + +The unique wedge: **this archive keeps the real failed submissions** (Wrong +Answer / TLE / Runtime Error / Compile Error) next to each accepted solution. +Every clean-solutions repo competes on count; this one competes on *honesty + +the lesson from each mistake*. Lean on that everywhere. + +--- + +## 1. Repo "About" (GitHub β†’ repo β†’ βš™ Edit) + +> 173 LeetCode problems solved in C++/Java/SQL β€” kept WITH my real Wrong-Answer +> & TLE submissions and the lesson from each. Searchable docs site + per-problem +> verdict history. + +(Keep it under 350 chars; lead with the wedge, not the count.) + +## 2. Topics (GitHub β†’ repo β†’ βš™ next to About β†’ add up to 20) + +Paste these 8 exact-match topics (high-intent search terms, all valid GitHub +topic slugs): + +``` +leetcode +leetcode-solutions +algorithms +sql +oracle-sql +interview-prep +coding-interview +data-structures +``` + +Optional extras if you want more surface: `cpp`, `competitive-programming`, +`leetcode-cpp`, `dsa`. + +## 3. Reddit β€” r/leetcode (the single best organic launch) + +Personal-story headlines pull ~3Γ— the upvotes of "I made X". Post Tue–Thu, +13:00–16:00 UTC. Build a little karma/comment first so it isn't your first post. + +**Title:** +> I made my LeetCode repo public WITH all my failed submissions + +**Body (paste, tweak the voice):** +> Most solution repos only show the clean accepted answer. I kept the opposite: +> every Wrong Answer, TLE, Runtime Error and Compile Error I submitted *before* +> getting each problem accepted β€” 173 problems, 542 submission files, each +> timestamped, in C++/Java/SQL. +> +> There's a searchable docs site where each problem page shows the full verdict +> history (Accepted β†’ WA β†’ TLE β†’ RE) with the code for each attempt in language +> tabs, plus a "Lessons / common mistakes" index of the 89 problems I did *not* +> get on the first try. +> +> Browse: https://leetcode-problems.pages.dev +> Repo: https://github.com/aliammari1/Leetcode_problems +> +> Honestly the failed attempts taught me more than the accepted ones. Curious if +> others keep their wrong submissions or delete them. + +- Reply to the first comments within the hour; that drives the ranking. +- Cross-post variants: r/cscareerquestions (only if it fits a discussion), + r/programming (riskier β€” needs the "why" framing, no naked self-promo). + +## 4. Awesome-list / directory submission PRs + +Compounding passive faucet. Open one PR per list; lead with the wedge line. + +**`awesome-leetcode-resources`** (e.g. ashishps1/awesome-leetcode-resources) β€” +under a "Solutions / Repositories" section: + +```markdown +- [aliammari1/Leetcode_problems](https://github.com/aliammari1/Leetcode_problems) β€” 173 problems in C++/Java/SQL kept WITH their real Wrong-Answer/TLE submission history and a per-problem "what went wrong" lesson; searchable mkdocs site. +``` + +**`awesome-competitive-programming`** (e.g. lnishan/awesome-competitive-programming) β€” +under "Solutions / Practice": + +```markdown +- [aliammari1/Leetcode_problems](https://github.com/aliammari1/Leetcode_problems) β€” A LeetCode solution archive that preserves every failed submission (Wrong Answer, TLE, Runtime Error) before each accept, across C++/Java/Oracle-SQL, with a searchable docs site. +``` + +Read each list's CONTRIBUTING + run its linter (often `awesome-lint`) before the +PR. Match the existing entry style exactly or it gets bounced. + +## 5. SEO (already shipped β€” what's working for you) + +- Per-problem docs page `` = exact `"<#>. <Problem> β€” Solution"` (the + literal phrase people search). 173 pages = 173 long-tail landing pages. +- `Lessons / common mistakes` index page targets the "common leetcode mistakes / + why is my submission wrong" intent that clean repos don't rank for. +- Cross-link footer on every page β†’ HackerRank archive + profile hub (internal + backlinks raise the whole portfolio's discoverability). +- Keep commits flowing (freshness signal) and re-run + `python scripts/generate_progress.py --docs` whenever you add problems. + +## 6. The MCP angle (Wave 2 β€” build later, mention now) + +`leetcode-solutions-mcp`: bundle this archive (problems + wrong-answer history) +as an **offline, no-auth MCP server** β€” rivals need a session cookie. Pitch: +*"the MCP that remembers what NOT to do."* When built: list on +punkpeye/awesome-mcp-servers, mcpmarket.com, and the official MCP registry, and +back-link it from this README. The README already teases it so the audience is +primed. + +--- + +### Execution order (highest ROI first) + +1. Set **About + 8 topics** (5 min, compounding SEO). +2. Post to **r/leetcode** with the story title (the spike). +3. Open the **awesome-leetcode-resources** + **awesome-competitive-programming** + PRs (passive forever). +4. Build + list the **MCP** (Wave 2, highest ceiling). diff --git a/README.md b/README.md index 0f147b6..5b8c773 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Leetcode Problems +# Every LeetCode problem I solved β€” including my real Wrong-Answer / TLE submissions and the lesson from each <!-- TODO: replace with the committed banner once generated (see BANNER.md + assets/). --> <!-- ![Leetcode Problems banner](assets/banner.png) --> @@ -8,11 +8,27 @@ [![Accepted](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fraw.githubusercontent.com%2Faliammari1%2FLeetcode_problems%2Fmain%2Fdocs%2Fprogress.json&query=%24.stats.accepted_pct&suffix=%25&label=accepted&color=success)](docs/progress.json) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) -A LeetCode solution archive organized by individual problem directories. What makes -it different from a typical solutions dump: it preserves the **real submission -history** β€” every Wrong Answer, Runtime Error, Time Limit Exceeded, and Compile -Error attempt that came before the accepted solution, each timestamped β€” across -C++, Java, Oracle SQL, and MySQL. +Not another clean-solutions dump. This archive of **173 problems / 542 files** +keeps the **real submission history** β€” every Wrong Answer, Runtime Error, Time +Limit Exceeded, and Compile Error attempt that came *before* the accepted +solution, each timestamped β€” across C++, Java, Oracle SQL, and MySQL. You can +read the *progression* of how each problem got solved, and the bug each failed +attempt tripped on. + +### πŸ”Ž [Browse all 173 problems (searchable) β†’](https://leetcode-problems.pages.dev) + +Per-problem pages show the **verdict history** (Accepted β†’ Wrong Answer β†’ TLE β†’ +Runtime Error, timestamped) with every language variant in tabs. See also the +[**Lessons / common mistakes**](https://leetcode-problems.pages.dev/lessons/) +index β€” the 89 problems I did *not* get on the first try. + +> **⭐ If preserving the failed attempts is useful to you, star the repo** β€” it +> helps others find an honest, mistakes-and-all study archive. + +> **Coming in Wave 2:** the **`leetcode-solutions` MCP** β€” a bundled, offline +> MCP server exposing all 173 problems *with their wrong-answer history* to any +> MCP-capable assistant, no session cookie required. *"The MCP that remembers +> what NOT to do."* ## πŸš€ Features @@ -404,4 +420,12 @@ Project Link: [https://github.com/aliammari1/Leetcode_problems](https://github.c --- +## πŸ”— Related projects + +- **[HackerRank solution archive](https://github.com/aliammari1/Hackerrank_problems)** β€” the companion archive, same honest, history-preserving format. +- **[All my projects (profile hub)](https://github.com/aliammari1)** β€” apps, tools, and games. +- **[LeetCode profile](https://leetcode.com/aliammari1/)** + +--- + *Happy Coding! πŸš€* \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index 8f85a70..683d8a8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -59,6 +59,7 @@ plugins: nav: - Home: index.md - All Problems: problems/index.md + - Lessons / common mistakes: lessons.md - Index by Language: tags.md extra: diff --git a/scripts/generate_progress.py b/scripts/generate_progress.py index dfc6e70..a756eb0 100644 --- a/scripts/generate_progress.py +++ b/scripts/generate_progress.py @@ -41,6 +41,21 @@ DOCS = ROOT / "docs" README = ROOT / "README.md" PROGRESS_JSON = DOCS / "progress.json" +NUMBERS_JSON = Path(__file__).resolve().parent / "problem_numbers.json" + + +def load_numbers() -> dict[str, int]: + """Slug -> canonical LeetCode problem number (for exact-match SEO titles). + + Optional file; missing slugs simply omit the number prefix. + """ + if not NUMBERS_JSON.is_file(): + return {} + raw = json.loads(NUMBERS_JSON.read_text(encoding="utf-8")) + return {k: v for k, v in raw.items() if not k.startswith("_")} + + +NUMBERS = load_numbers() # Maps file extension -> (display language, mkdocs content-tab fence language). EXT_LANG = { @@ -92,6 +107,20 @@ def url(self) -> str: def title(self) -> str: return self.slug.replace("-", " ").title() + @property + def number(self) -> int | None: + return NUMBERS.get(self.slug) + + @property + def seo_title(self) -> str: + """Exact-match page title for search: '<#>. <Problem> β€” Solution'. + + Falls back to '<Problem> β€” Solution' when the number is unknown. + """ + if self.number is not None: + return f"{self.number}. {self.title} β€” Solution" + return f"{self.title} β€” Solution" + @property def accepted(self) -> bool: return any(s.verdict == "Accepted" for s in self.submissions) @@ -286,12 +315,29 @@ def build_docs(problems: dict[str, Problem], stats: dict) -> None: write_problems_index(pages / "index.md", problems) write_tags_index(DOCS / "tags.md", problems) + write_lessons_index(DOCS / "lessons.md", problems) write_docs_home(DOCS / "index.md", stats, problems) +# Verdicts that are "wrong attempts" worth a lesson callout. +WRONG_VERDICTS = ("Wrong Answer", "Time Limit Exceeded", "Runtime Error", "Compile Error") + +# Cross-link footer (Related projects + profile hub) appended to every page. +RELATED_FOOTER = "\n".join( + [ + "---", + "", + "**Related:** " + "[HackerRank solution archive](https://github.com/aliammari1/Hackerrank_problems) Β· " + "[All my projects (profile hub)](https://github.com/aliammari1) Β· " + "[LeetCode profile](https://leetcode.com/aliammari1/)", + ] +) + + def write_problem_page(path: Path, prob: Problem) -> None: lines = [ - f"# {prob.title}", + f"# {prob.seo_title}", "", f"[:material-open-in-new: Open on LeetCode]({prob.url}){{ .md-button }}", "", @@ -327,15 +373,58 @@ def write_problem_page(path: Path, prob: Problem) -> None: lines.append(f" {codeline}") lines.append(" ```") lines.append("") + lines.append(RELATED_FOOTER) + lines.append("") path.write_text("\n".join(lines) + "\n", encoding="utf-8") def write_problems_index(path: Path, problems: dict[str, Problem]) -> None: - lines = ["# All Problems", "", "| Problem | Status | Languages |", "|---------|--------|-----------|"] + lines = ["# All Problems", "", "| # | Problem | Status | Languages |", "|---|---------|--------|-----------|"] for slug, prob in sorted(problems.items()): status = "βœ… Accepted" if prob.accepted else "⏳ Attempted" langs = ", ".join(prob.languages) - lines.append(f"| [{prob.title}]({slug}.md) | {status} | {langs} |") + num = str(prob.number) if prob.number is not None else "β€”" + lines.append(f"| {num} | [{prob.title}]({slug}.md) | {status} | {langs} |") + lines.append("") + path.write_text("\n".join(lines) + "\n", encoding="utf-8") + + +def write_lessons_index(path: Path, problems: dict[str, Problem]) -> None: + """Index of problems that have real failed attempts before the accept. + + This is the archive's signature page: the slugs where there is a documented + Wrong Answer / TLE / Runtime Error / Compile Error to learn from. + """ + rows: list[tuple[Problem, Counter[str]]] = [] + for prob in problems.values(): + wrong: Counter[str] = Counter() + for s in prob.submissions: + if s.verdict in WRONG_VERDICTS: + wrong[s.verdict] += 1 + if wrong: + rows.append((prob, wrong)) + + rows.sort(key=lambda r: (-sum(r[1].values()), r[0].slug)) + total_wrong = sum(sum(w.values()) for _, w in rows) + + lines = [ + "# Lessons / common mistakes", + "", + "The point of this archive: **every problem below was *not* solved on the", + "first try.** These pages keep the failed submissions β€” Wrong Answer, Time", + "Limit Exceeded, Runtime Error, Compile Error β€” next to the accepted one, so", + "you can read *what went wrong and why* instead of only a clean final answer.", + "", + f"- **Problems with at least one failed attempt:** {len(rows)}", + f"- **Total failed submissions preserved:** {total_wrong}", + "", + "| # | Problem | Failed attempts (by verdict) |", + "|---|---------|------------------------------|", + ] + for prob, wrong in rows: + num = str(prob.number) if prob.number is not None else "β€”" + detail = ", ".join(f"{v}: {wrong[v]}" for v in VERDICT_ORDER if wrong.get(v)) + lines.append(f"| {num} | [{prob.title}](problems/{prob.slug}.md) | {detail} |") lines.append("") path.write_text("\n".join(lines) + "\n", encoding="utf-8") @@ -357,12 +446,21 @@ def write_tags_index(path: Path, problems: dict[str, Problem]) -> None: def write_docs_home(path: Path, stats: dict, problems: dict[str, Problem]) -> None: + n = stats["unique_slugs"] lines = [ "# LeetCode Solutions Archive", "", - "A personal LeetCode archive with **real submission history** β€” including the", - "wrong-answer, runtime-error, and TLE attempts that came before each accepted", - "solution. Browse problems below or jump to the [index by language](tags.md).", + "Every LeetCode problem I solved β€” **including my real Wrong-Answer / TLE", + "submissions and the lesson from each.** Not a clean-solutions dump: each page", + "keeps the failed attempts next to the accepted one so you can read the", + "*progression*, not just the final answer.", + "", + f"[:material-magnify: Browse all {n} problems (searchable)]" + "(problems/index.md){ .md-button .md-button--primary }", + "[:material-lightbulb-on: Lessons / common mistakes](lessons.md){ .md-button }", + "", + "> Use the search box (top right) to jump straight to any problem by number or", + "> name.", "", "## At a glance", "", @@ -375,7 +473,18 @@ def write_docs_home(path: Path, stats: dict, problems: dict[str, Problem]) -> No ] for lang in stats["languages"]: lines.append(f"| {lang['language']} | {lang['files']} | {lang['slugs']} |") - lines += ["", "[Browse all problems](problems/index.md){ .md-button .md-button--primary }", ""] + lines += [ + "", + "## Coming soon: the `leetcode-solutions` MCP (Wave 2)", + "", + "A bundled, offline **MCP server** that exposes this archive β€” 173 problems", + "*with their real wrong-answer history* β€” to any MCP-capable assistant. No", + "session cookie required (rivals need one). *\"The MCP that remembers what NOT", + "to do.\"*", + "", + RELATED_FOOTER, + "", + ] path.write_text("\n".join(lines) + "\n", encoding="utf-8") diff --git a/scripts/problem_numbers.json b/scripts/problem_numbers.json new file mode 100644 index 0000000..74ae9bc --- /dev/null +++ b/scripts/problem_numbers.json @@ -0,0 +1,176 @@ +{ + "_comment": "Canonical LeetCode problem numbers per slug. Used by generate_progress.py to build exact-match SEO page titles ('<#>. <Problem> β€” Solution'). Slugs absent here fall back to '<Problem> β€” Solution'. Keep alphabetical by slug.", + "add-binary": 67, + "add-two-integers": 2235, + "add-two-numbers": 2, + "array-partition": 561, + "article-views-i": 1148, + "average-salary-excluding-the-minimum-and-maximum-salary": 1484, + "average-time-of-process-per-machine": 1661, + "backspace-string-compare": 844, + "baseball-game": 682, + "best-time-to-buy-and-sell-stock": 121, + "best-time-to-buy-and-sell-stock-ii": 122, + "big-countries": 595, + "binary-search": 704, + "binary-search-tree-iterator": 173, + "binary-tree-inorder-traversal": 94, + "binary-tree-level-order-traversal": 102, + "binary-tree-postorder-traversal": 145, + "binary-tree-preorder-traversal": 144, + "binary-tree-right-side-view": 199, + "build-array-from-permutation": 1920, + "can-make-arithmetic-progression-from-sequence": 1502, + "can-place-flowers": 605, + "check-if-a-string-is-an-acronym-of-words": 2828, + "check-if-n-and-its-double-exist": 1346, + "climbing-stairs": 70, + "combine-two-tables": 175, + "concatenation-of-array": 1929, + "contains-duplicate": 217, + "convert-binary-number-in-a-linked-list-to-integer": 1290, + "convert-the-temperature": 2469, + "copy-list-with-random-pointer": 138, + "count-odd-numbers-in-an-interval-range": 1523, + "count-pairs-whose-sum-is-less-than-target": 2824, + "customers-who-never-order": 183, + "customer-who-visited-but-did-not-make-any-transactions": 1581, + "daily-leads-and-partners": 1693, + "daily-temperatures": 739, + "delete-duplicate-emails": 196, + "delete-the-middle-node-of-a-linked-list": 2095, + "design-circular-queue": 622, + "design-linked-list": 707, + "determine-the-minimum-sum-of-a-k-avoiding-array": 2829, + "diagonal-traverse": 498, + "duplicate-emails": 182, + "duplicate-zeros": 1089, + "employee-bonus": 577, + "employees-earning-more-than-their-managers": 181, + "fibonacci-number": 509, + "find-all-numbers-disappeared-in-an-array": 448, + "find-customer-referee": 584, + "find-numbers-with-even-number-of-digits": 1295, + "find-pivot-index": 724, + "find-the-difference": 389, + "find-the-index-of-the-first-occurrence-in-a-string": 28, + "find-total-time-spent-by-each-employee": 1741, + "first-bad-version": 278, + "fizz-buzz": 412, + "flatten-a-multilevel-doubly-linked-list": 430, + "game-play-analysis-i": 511, + "greatest-common-divisor-of-strings": 1071, + "group-anagrams": 49, + "guess-number-higher-or-lower": 374, + "height-checker": 1051, + "html-entity-parser": 1410, + "increasing-triplet-subsequence": 334, + "insert-greatest-common-divisors-in-linked-list": 2807, + "insert-into-a-binary-search-tree": 701, + "intersection-of-two-linked-lists": 160, + "invalid-tweets": 1683, + "isomorphic-strings": 205, + "is-subsequence": 392, + "kids-with-the-greatest-number-of-candies": 1431, + "k-th-symbol-in-grammar": 779, + "largest-number-at-least-twice-of-others": 747, + "largest-perimeter-triangle": 976, + "length-of-last-word": 58, + "linked-list-cycle": 141, + "linked-list-cycle-ii": 142, + "longest-common-prefix": 14, + "majority-element": 169, + "managers-with-at-least-5-direct-reports": 570, + "matrix-diagonal-sum": 1572, + "max-consecutive-ones": 485, + "maximum-average-subarray-i": 643, + "maximum-depth-of-binary-tree": 104, + "maximum-depth-of-n-ary-tree": 559, + "maximum-twin-sum-of-a-linked-list": 2130, + "max-pair-sum-in-an-array": 2815, + "median-of-two-sorted-arrays": 4, + "merge-nodes-in-between-zeros": 2181, + "merge-sorted-array": 88, + "merge-strings-alternately": 1768, + "merge-two-sorted-lists": 21, + "middle-of-the-linked-list": 876, + "minimum-size-subarray-sum": 209, + "min-stack": 155, + "monotonic-array": 896, + "move-zeroes": 283, + "n-ary-tree-level-order-traversal": 429, + "n-ary-tree-postorder-traversal": 590, + "n-ary-tree-preorder-traversal": 589, + "not-boring-movies": 620, + "number-of-1-bits": 191, + "number-of-employees-who-met-the-target": 2890, + "number-of-good-pairs": 1512, + "number-of-steps-to-reduce-a-number-to-zero": 1342, + "number-of-unique-subjects-taught-by-each-teacher": 2356, + "odd-even-linked-list": 328, + "palindrome-linked-list": 234, + "palindrome-number": 9, + "pascals-triangle": 118, + "pascals-triangle-ii": 119, + "path-sum": 112, + "plus-one": 66, + "populating-next-right-pointers-in-each-node": 116, + "populating-next-right-pointers-in-each-node-ii": 117, + "power-of-four": 342, + "power-of-three": 326, + "power-of-two": 231, + "powx-n": 50, + "product-of-array-except-self": 238, + "product-sales-analysis-i": 1068, + "recyclable-and-low-fat-products": 1757, + "remove-duplicates-from-sorted-array": 26, + "remove-duplicates-from-sorted-array-ii": 80, + "remove-duplicates-from-sorted-list": 83, + "remove-duplicates-from-sorted-list-ii": 82, + "remove-element": 27, + "remove-linked-list-elements": 203, + "remove-nth-node-from-end-of-list": 19, + "repeated-substring-pattern": 459, + "replace-elements-with-greatest-element-on-right-side": 1299, + "replace-employee-id-with-the-unique-identifier": 1378, + "reverse-integer": 7, + "reverse-linked-list": 206, + "reverse-string": 344, + "reverse-vowels-of-a-string": 345, + "reverse-words-in-a-string": 151, + "reverse-words-in-a-string-iii": 557, + "richest-customer-wealth": 1672, + "rising-temperature": 197, + "robot-return-to-origin": 657, + "roman-to-integer": 13, + "rotate-array": 189, + "rotate-list": 61, + "running-sum-of-1d-array": 1480, + "same-tree": 100, + "score-of-a-string": 3110, + "search-a-2d-matrix": 74, + "search-in-a-binary-search-tree": 700, + "search-in-rotated-sorted-array": 33, + "search-insert-position": 35, + "sign-of-the-product-of-an-array": 1822, + "single-number": 136, + "sort-array-by-parity": 905, + "sort-characters-by-frequency": 451, + "spiral-matrix": 54, + "sqrtx": 69, + "squares-of-a-sorted-array": 977, + "string-to-integer-atoi": 8, + "students-and-examinations": 1280, + "subtract-the-product-and-sum-of-digits-of-an-integer": 1281, + "swap-nodes-in-pairs": 24, + "symmetric-tree": 101, + "third-maximum-number": 414, + "to-lower-case": 709, + "two-sum": 1, + "two-sum-ii-input-array-is-sorted": 167, + "unique-number-of-occurrences": 1207, + "valid-anagram": 242, + "valid-mountain-array": 941, + "valid-parentheses": 20, + "validate-binary-search-tree": 98 +} From e2b65bfeff8658f624e599199054cdc377d0b7b1 Mon Sep 17 00:00:00 2001 From: Ali Ammari <ammari.ali.0001@gmail.com> Date: Fri, 12 Jun 2026 15:19:36 +0100 Subject: [PATCH 9/9] chore: keep internal growth/marketing notes out of the repo Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --- .gitignore | 8 ++++ BANNER.md | 48 --------------------- GROWTH.md | 119 ----------------------------------------------------- 3 files changed, 8 insertions(+), 167 deletions(-) delete mode 100644 BANNER.md delete mode 100644 GROWTH.md diff --git a/.gitignore b/.gitignore index cc0b353..af6485f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,11 @@ venv/ # OS .DS_Store Thumbs.db + +# Internal growth/marketing notes (not for public) +GROWTH.md +BANNER.md +SUBMITTING_TO_AWESOME.md +launch-kit.md +star-strategy.md +modernization-backlog.md diff --git a/BANNER.md b/BANNER.md deleted file mode 100644 index 38b882f..0000000 --- a/BANNER.md +++ /dev/null @@ -1,48 +0,0 @@ -# Banner / social preview - -The repository banner and GitHub social-preview image are generated from the -**single image-generation prompt** below. Run it, then commit the outputs under -`assets/` and uncomment the banner line at the top of `README.md`. - -## Deliverables - -- `assets/banner.png` β€” wide README hero (referenced by the commented-out line - at the top of `README.md` as `assets/banner.png`). -- `assets/social-preview.png` β€” **1280Γ—640**, set via GitHub β†’ Settings β†’ - Social preview. - -Commit the results as local PNG/SVG under `assets/` (never hot-link, to avoid -404 / rate-limit). Keep the same composition for both sizes so they read as one -identity. - -## The prompt (use verbatim) - -> A dark, near-black technical banner for a LeetCode solution archive. The -> background is a dense **pattern grid / submission heatmap**: small square -> monochrome tiles in a tight matrix. Most tiles are muted dark-grey; -> **accent tiles glow amber and green for "Accepted"**, with a scattering of -> **muted red / orange tiles marking the Wrong-Answer, Runtime-Error, and -> Time-Limit-Exceeded attempts** β€” conveying the hook *"real submission -> history, including the wrong attempts."* -> -> Overlaid on the grid, **14 labelled pattern tiles** in a clean mono/technical -> typeface, one per algorithm family: **Arrays, Two Pointers, Sliding Window, -> Binary Search, Hashing, Stack, Linked List, Trees, BFS/DFS, Divide & -> Conquer, Greedy, Dynamic Programming, Math, SQL**. -> -> A compact language strip with three marks: **C++**, **Java**, **Oracle SQL**. -> -> Bold headline text: **"173 problems Β· 542 submissions"**. Small subtitle: -> *"every accepted solution β€” and the wrong attempts before it."* -> -> Aesthetic: editorial dark-tech, high contrast, generous spacing, amber accent -> (#FFB300-ish) on near-black (#0B0B0D), no clutter, no photographic elements, -> crisp pixel-grid feel. -> -> Render two crops sharing this exact composition: a **1280Γ—640** social card -> (`social-preview.png`) and a **wide README hero** banner (`banner.png`). - -## How to generate - -Use the `brandkit` Claude skill for the 1280Γ—640 social card and -`imagegen-frontend-web` for the wide README hero, feeding both the prompt above. diff --git a/GROWTH.md b/GROWTH.md deleted file mode 100644 index 7fcfcd6..0000000 --- a/GROWTH.md +++ /dev/null @@ -1,119 +0,0 @@ -# GROWTH.md β€” distribution kit for `Leetcode_problems` - -Internal, not part of the published docs. Everything here needs **your GitHub / -Reddit account** to execute. The repo content (README hook, mkdocs site, lessons -index, SEO titles, cross-links) is already done on `feat/upgrade`. - -The unique wedge: **this archive keeps the real failed submissions** (Wrong -Answer / TLE / Runtime Error / Compile Error) next to each accepted solution. -Every clean-solutions repo competes on count; this one competes on *honesty + -the lesson from each mistake*. Lean on that everywhere. - ---- - -## 1. Repo "About" (GitHub β†’ repo β†’ βš™ Edit) - -> 173 LeetCode problems solved in C++/Java/SQL β€” kept WITH my real Wrong-Answer -> & TLE submissions and the lesson from each. Searchable docs site + per-problem -> verdict history. - -(Keep it under 350 chars; lead with the wedge, not the count.) - -## 2. Topics (GitHub β†’ repo β†’ βš™ next to About β†’ add up to 20) - -Paste these 8 exact-match topics (high-intent search terms, all valid GitHub -topic slugs): - -``` -leetcode -leetcode-solutions -algorithms -sql -oracle-sql -interview-prep -coding-interview -data-structures -``` - -Optional extras if you want more surface: `cpp`, `competitive-programming`, -`leetcode-cpp`, `dsa`. - -## 3. Reddit β€” r/leetcode (the single best organic launch) - -Personal-story headlines pull ~3Γ— the upvotes of "I made X". Post Tue–Thu, -13:00–16:00 UTC. Build a little karma/comment first so it isn't your first post. - -**Title:** -> I made my LeetCode repo public WITH all my failed submissions - -**Body (paste, tweak the voice):** -> Most solution repos only show the clean accepted answer. I kept the opposite: -> every Wrong Answer, TLE, Runtime Error and Compile Error I submitted *before* -> getting each problem accepted β€” 173 problems, 542 submission files, each -> timestamped, in C++/Java/SQL. -> -> There's a searchable docs site where each problem page shows the full verdict -> history (Accepted β†’ WA β†’ TLE β†’ RE) with the code for each attempt in language -> tabs, plus a "Lessons / common mistakes" index of the 89 problems I did *not* -> get on the first try. -> -> Browse: https://leetcode-problems.pages.dev -> Repo: https://github.com/aliammari1/Leetcode_problems -> -> Honestly the failed attempts taught me more than the accepted ones. Curious if -> others keep their wrong submissions or delete them. - -- Reply to the first comments within the hour; that drives the ranking. -- Cross-post variants: r/cscareerquestions (only if it fits a discussion), - r/programming (riskier β€” needs the "why" framing, no naked self-promo). - -## 4. Awesome-list / directory submission PRs - -Compounding passive faucet. Open one PR per list; lead with the wedge line. - -**`awesome-leetcode-resources`** (e.g. ashishps1/awesome-leetcode-resources) β€” -under a "Solutions / Repositories" section: - -```markdown -- [aliammari1/Leetcode_problems](https://github.com/aliammari1/Leetcode_problems) β€” 173 problems in C++/Java/SQL kept WITH their real Wrong-Answer/TLE submission history and a per-problem "what went wrong" lesson; searchable mkdocs site. -``` - -**`awesome-competitive-programming`** (e.g. lnishan/awesome-competitive-programming) β€” -under "Solutions / Practice": - -```markdown -- [aliammari1/Leetcode_problems](https://github.com/aliammari1/Leetcode_problems) β€” A LeetCode solution archive that preserves every failed submission (Wrong Answer, TLE, Runtime Error) before each accept, across C++/Java/Oracle-SQL, with a searchable docs site. -``` - -Read each list's CONTRIBUTING + run its linter (often `awesome-lint`) before the -PR. Match the existing entry style exactly or it gets bounced. - -## 5. SEO (already shipped β€” what's working for you) - -- Per-problem docs page `<title>` = exact `"<#>. <Problem> β€” Solution"` (the - literal phrase people search). 173 pages = 173 long-tail landing pages. -- `Lessons / common mistakes` index page targets the "common leetcode mistakes / - why is my submission wrong" intent that clean repos don't rank for. -- Cross-link footer on every page β†’ HackerRank archive + profile hub (internal - backlinks raise the whole portfolio's discoverability). -- Keep commits flowing (freshness signal) and re-run - `python scripts/generate_progress.py --docs` whenever you add problems. - -## 6. The MCP angle (Wave 2 β€” build later, mention now) - -`leetcode-solutions-mcp`: bundle this archive (problems + wrong-answer history) -as an **offline, no-auth MCP server** β€” rivals need a session cookie. Pitch: -*"the MCP that remembers what NOT to do."* When built: list on -punkpeye/awesome-mcp-servers, mcpmarket.com, and the official MCP registry, and -back-link it from this README. The README already teases it so the audience is -primed. - ---- - -### Execution order (highest ROI first) - -1. Set **About + 8 topics** (5 min, compounding SEO). -2. Post to **r/leetcode** with the story title (the spike). -3. Open the **awesome-leetcode-resources** + **awesome-competitive-programming** - PRs (passive forever). -4. Build + list the **MCP** (Wave 2, highest ceiling).