docs(docs): add new problem documentation pages and source selector component

This commit is contained in:
Prad Nukala committed 2026-10-07 12:58:59 -04:00
1 parent 598688977a
commit 5627d0ca64
9 files changed
+321 -6

No files matched your search

+3 -6
View File
@@ -142,11 +142,8 @@ export default defineConfig({
sidebar: { sidebar: {
display: "flat", // "flat" | "group" | "page" display: "flat", // "flat" | "group" | "page"
}, },
// The glossary is no content folder any more, so it is pinned above the // No `featured`: Blume renders it above the `Sidebar` slot, and the
// sidebar tree, on every page and breakpoint. // content-source selector must sit above the pinned links, so
featured: [ // components/SourceSidebar.astro renders both (Changelog, Appendix).
{ label: "Changelog", href: "/changelog", icon: "clock" },
{ label: "Appendix", href: "/appendix", icon: "library" },
],
}, },
}); });
+3
View File
@@ -16,5 +16,8 @@ export default defineComponents({
layout: { layout: {
// Topic landing pages' "what links here": every problem pointing at the page. // Topic landing pages' "what links here": every problem pointing at the page.
PageFooter: "./components/BacklinksList.astro", PageFooter: "./components/BacklinksList.astro",
// Content-source selector + pinned links above the nav tree, scoped to the
// active source (sources.ts).
Sidebar: "./components/SourceSidebar.astro",
}, },
}); });
+117
View File
@@ -0,0 +1,117 @@
---
/**
* The `Sidebar` slot (also the mobile drawer's nav, which defaults to it): a
* Fumadocs-style content-source selector, then the pinned links, then
* Blume's own nav tree scoped to the active source (`sources.ts`).
*
* The pinned links live here, not in `navigation.featured`: Blume renders
* featured links above this slot, so the selector could not sit over them.
*
* The selector is a `<details>` with Blume's `data-blume-dropdown` light
* dismiss (outside press, Escape, focus leaving), like its own NavSelector.
*/
import Icon from "blume/components/Icon.astro";
import { withBase, withMountedBase } from "blume/components/islands/base-path.ts";
import NavTree from "blume/components/layout/NavTree.astro";
import type { UIStrings } from "blume/core/i18n-ui.ts";
import { isUnderPath } from "blume/core/navigation.ts";
import type { NavNode } from "blume/core/types.ts";
import { SOURCES } from "../sources.ts";
interface Props {
items: NavNode[];
currentRoute: string;
strings?: UIStrings["nav"];
ids?: Map<NavNode, string>;
fragmentBase?: string;
}
const PINNED = [
{ label: "Changelog", href: "/changelog", icon: "clock" },
{ label: "Appendix", href: "/appendix", icon: "library" },
];
const { items, currentRoute, ...tree } = Astro.props;
const active =
SOURCES.filter((s) => isUnderPath(currentRoute, s.path)).sort(
(a, b) => b.path.length - a.path.length,
)[0] ?? SOURCES[0]!;
const claims = (node: NavNode, path: string) => {
const at = node.kind === "page" ? node.route : node.path;
return at !== undefined && isUnderPath(at, path);
};
// The root source keeps every node no other source claims; any other source
// shows its folder's children, as if that folder were the content root.
const scoped =
active.path === "/"
? items.filter(
(node) => !SOURCES.some((s) => s.path !== "/" && claims(node, s.path)),
)
: items.flatMap((node) =>
node.kind === "group" && node.path === active.path ? node.children : [],
);
---
<details class="group relative mb-3" data-blume-dropdown>
<summary
aria-label={`Content source: ${active.label}`}
class="flex w-full cursor-pointer list-none items-center gap-2.5 rounded-xl border border-border bg-muted/50 p-1.5 pe-2.5 text-foreground text-sm transition-colors hover:bg-muted group-open:bg-muted [&::-webkit-details-marker]:hidden"
>
<span class="flex size-7 shrink-0 items-center justify-center rounded-lg border border-border bg-background">
<Icon name={active.icon} size={15} />
</span>
<span class="min-w-0 flex-1 truncate font-medium">{active.label}</span>
<Icon class="shrink-0 text-muted-foreground" name="chevrons-up-down" size={14} />
</summary>
<div
class="absolute inset-x-0 z-50 mt-1.5 rounded-xl border border-border bg-background p-1 shadow-xl"
data-blume-dropdown-panel
>
{
SOURCES.map((source) => (
<a
aria-current={source === active ? "true" : undefined}
class="flex items-start gap-2.5 rounded-lg p-2 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=true]:text-foreground"
href={withMountedBase(source.path)}
>
<Icon class="mt-0.5 shrink-0" name={source.icon} size={16} />
<span class="min-w-0 flex-1">
<span class="block font-medium">{source.label}</span>
<span class="block text-muted-foreground text-xs">{source.description}</span>
</span>
{source === active && <Icon class="mt-0.5 shrink-0" name="check" size={14} />}
</a>
))
}
</div>
</details>
<nav aria-label="Featured" class="mb-4 border-border border-b pb-4">
<ul class="m-0 list-none p-0">
{
PINNED.map((link) => (
<li>
<a
aria-current={currentRoute === link.href ? "page" : undefined}
class="flex items-center gap-2 rounded-[0.65rem] px-2.5 py-1.5 text-muted-foreground text-sm transition-colors hover:bg-muted hover:text-foreground aria-[current=page]:bg-muted aria-[current=page]:text-foreground"
href={withBase(link.href)}
>
<Icon class="shrink-0" name={link.icon} size={14} />
<span class="flex-1 truncate">{link.label}</span>
</a>
</li>
))
}
</ul>
</nav>
<NavTree {...tree} currentRoute={currentRoute} items={scoped} />
<script>
import { installDropdownDismiss } from "blume/components/dropdown-dismiss.ts";
installDropdownDismiss();
</script>
@@ -0,0 +1,45 @@
---
title: '901. Online Stock Span'
description: Design an algorithm that collects daily price quotes for some stock and returns the span of that stock's price for the current day
icon: dot
topics:
- { name: "Stack", slug: "stack" }
- { name: "Design", slug: "design" }
- { name: "Monotonic Stack", slug: "monotonic-stack" }
- { name: "Data Stream", slug: "data-stream" }
issue: "https://github.com/prdlk/leetcode/issues/75"
sidebar:
label: 'Online Stock Span'
badge: 'Medium'
---
### Example 1:
- Input: ``
- Output: ``
- Explanation: Input ["StockSpanner", "next", "next", "next", "next", "next", "next", "next"] `[[]`, `[100]`, `[80]`, `[60]`, `[70]`, `[60]`, `[75]`, [85]] Output [null, `1`, `1`, `1`, `2`, `1`, `4`, 6] Explanation StockSpanner stockSpanner = new StockSpanner(); stockSpanner.next(100); // return `1` stockSpanner.next(80); // return `1` stockSpanner.next(60); // return `1` stockSpanner.next(70); // return `2` stockSpanner.next(60); // return `1` stockSpanner.next(75); // return `4`, because the last `4` prices (including today's `price` of `75`) were less than or equal to today's `price`. stockSpanner.next(85); // return `6`
### Constraints:
- `1 <= price <= 10^5`
- At most 10^4 calls will be made to next.
## Solution
```py
class StockSpanner:
def __init__(self):
self.stack = []
def next(self, price: int) -> int:
ans = 1
while self.stack and self.stack[-1][0] <= price:
ans += self.stack.pop()[1]
self.stack.append([price, ans])
return ans
# Your StockSpanner object will be instantiated and called as such:
# obj = StockSpanner()
# param_1 = obj.next(price)
```
@@ -0,0 +1,46 @@
---
title: '76. Minimum Window Substring'
description: Given two strings s and t of lengths m and n respectively, return the minimum window substring of s such that every character in t (including duplicates) is included in the window. If there is no such substring, return the empty string ""
icon: dot
topics:
- { name: "Hash Table", slug: "hash-table" }
- { name: "String", slug: "string" }
- { name: "Sliding Window", slug: "sliding-window" }
issue: "https://github.com/prdlk/leetcode/issues/57"
sidebar:
label: 'Minimum Window Substring'
badge: 'Hard'
---
::::warning
Could you find an algorithm that runs in O(m + n) time?
::::
### Example 1:
- Input: `s = "ADOBECODEBANC", t = "ABC"`
- Output: `"BANC"`
- Explanation: The minimum window substring "BANC" includes 'A', 'B', and 'C' from string `t`.
### Example 2:
- Input: `s = "a", t = "a"`
- Output: `"a"`
- Explanation: The entire string `s` is the minimum window.
### Example 3:
- Input: `s = "a", t = "aa"`
- Output: `""`
- Explanation: Both 'a's from `t` must be included in the window. Since the largest window of `s` only has one 'a', return empty string.
### Constraints:
- `m == s.length`
- `n == t.length`
- `1 <= m, n <= 10^5`
- `s` and `t` consist of uppercase and lowercase English letters.
## Solution
```py
class Solution:
def minWindow(self, s: str, t: str) -> str:
```
@@ -0,0 +1,43 @@
---
title: '224. Basic Calculator'
description: Given a string s representing a valid expression, implement a basic calculator to evaluate it, and return the result of the evaluation
icon: dot
topics:
- { name: "Math", slug: "math" }
- { name: "String", slug: "string" }
- { name: "Stack", slug: "stack" }
- { name: "Recursion", slug: "recursion" }
issue: "https://github.com/prdlk/leetcode/issues/71"
sidebar:
label: 'Basic Calculator'
badge: 'Hard'
---
### Example 1:
- Input: `s = "1 + 1"`
- Output: `2`
### Example 2:
- Input: `s = " 2-1 + 2 "`
- Output: `3`
### Example 3:
- Input: `s = "(1+(4+5+2)-3)+(6+8)"`
- Output: `23`
### Constraints:
- `1 <= s.length <= 3 * 10^5`
- `s` consists of digits, '+', '-', '(', ')', and ' '.
- `s` represents a valid expression.
- '+' is not used as a unary operation (i.e., "+1" and "+(2 + 3)" is invalid).
- '-' could be used as a unary operation (i.e., "-1" and "-(2 + 3)" is valid).
- There will be no two consecutive operators in the input.
- Every number and running calculation will fit in a signed 32-bit integer.
## Solution
```py
class Solution:
def calculate(self, s: str) -> int:
```
+27
View File
@@ -0,0 +1,27 @@
---
title: System Design
description: Notes for system design interviews — how to scope a problem, size it, and pick the parts that carry the load.
icon: network
sidebar:
label: Overview
---
## How an interview runs
<Steps>
<Step title="Requirements">
Pin down the functional requirements (what the system does) and the non-functional ones (latency, availability, consistency, scale) before drawing anything.
</Step>
<Step title="Estimates">
Size the traffic, storage and bandwidth with back-of-the-envelope numbers, so later choices have a reason.
</Step>
<Step title="API and data model">
Define the endpoints and the entities they read and write.
</Step>
<Step title="High-level design">
Draw the request path end to end: clients, load balancers, services, caches, stores and queues.
</Step>
<Step title="Deep dives">
Take the hardest part (a hot key, fan-out, a consistency guarantee) and work through its tradeoffs.
</Step>
</Steps>
+3
View File
@@ -29,6 +29,7 @@ export default [
{ from: "/71", to: "/stack/simplify-path" }, { from: "/71", to: "/stack/simplify-path" },
{ from: "/73", to: "/matrix-index-math/set-matrix-zeroes" }, { from: "/73", to: "/matrix-index-math/set-matrix-zeroes" },
{ from: "/74", to: "/binary-search/search-a-2d-matrix" }, { from: "/74", to: "/binary-search/search-a-2d-matrix" },
{ from: "/76", to: "/sliding-window/minimum-window-substring" },
{ from: "/78", to: "/backtracking/subsets" }, { from: "/78", to: "/backtracking/subsets" },
{ from: "/80", to: "/two-pointers/remove-duplicates-from-sorted-array-ii" }, { from: "/80", to: "/two-pointers/remove-duplicates-from-sorted-array-ii" },
{ from: "/84", to: "/monotonic-stack/largest-rectangle-in-histogram" }, { from: "/84", to: "/monotonic-stack/largest-rectangle-in-histogram" },
@@ -61,6 +62,7 @@ export default [
{ from: "/211", to: "/trie/design-add-and-search-words-data-structure" }, { from: "/211", to: "/trie/design-add-and-search-words-data-structure" },
{ from: "/215", to: "/heap-priority-queue/kth-largest-element-in-an-array" }, { from: "/215", to: "/heap-priority-queue/kth-largest-element-in-an-array" },
{ from: "/217", to: "/hash-table/contains-duplicate" }, { from: "/217", to: "/hash-table/contains-duplicate" },
{ from: "/224", to: "/stack/basic-calculator" },
{ from: "/226", to: "/tree-dfs/invert-binary-tree" }, { from: "/226", to: "/tree-dfs/invert-binary-tree" },
{ from: "/230", to: "/binary-search-tree/kth-smallest-element-in-a-bst" }, { from: "/230", to: "/binary-search-tree/kth-smallest-element-in-a-bst" },
{ from: "/235", to: "/binary-search-tree/lowest-common-ancestor-of-a-binary-search-tree" }, { from: "/235", to: "/binary-search-tree/lowest-common-ancestor-of-a-binary-search-tree" },
@@ -89,6 +91,7 @@ export default [
{ from: "/739", to: "/monotonic-stack/daily-temperatures" }, { from: "/739", to: "/monotonic-stack/daily-temperatures" },
{ from: "/853", to: "/monotonic-stack/car-fleet" }, { from: "/853", to: "/monotonic-stack/car-fleet" },
{ from: "/875", to: "/binary-search/koko-eating-bananas" }, { from: "/875", to: "/binary-search/koko-eating-bananas" },
{ from: "/901", to: "/monotonic-stack/online-stock-span" },
{ from: "/973", to: "/heap-priority-queue/k-closest-points-to-origin" }, { from: "/973", to: "/heap-priority-queue/k-closest-points-to-origin" },
{ from: "/974", to: "/prefix-sum/subarray-sums-divisible-by-k" }, { from: "/974", to: "/prefix-sum/subarray-sums-divisible-by-k" },
{ from: "/977", to: "/two-pointers/squares-of-a-sorted-array" }, { from: "/977", to: "/two-pointers/squares-of-a-sorted-array" },
+34
View File
@@ -0,0 +1,34 @@
/**
* The site's content sources — what the sidebar's source selector switches
* between (`components/SourceSidebar.astro`), Fumadocs' "root folders". A
* source owns every route under its `path`, and the sidebar shows only the
* active source's pages. The source at `/` owns whatever no other source
* claims, which is how the LeetCode campaign keeps its routes (`/tree-dfs/…`)
* while a later source mounts beside it.
*
* Adding one: a content folder at the path (`content/system-design/`) with an
* `index.mdx`, so the selector's link is a page, plus a row here. The active
* source is the deepest `path` the current route sits under.
*/
export interface Source {
label: string;
description: string;
/** A Blume built-in icon name. */
icon: string;
path: string;
}
export const SOURCES: Source[] = [
{
label: "LeetCode",
description: "Data structures & algorithms",
icon: "binary",
path: "/",
},
{
label: "System Design",
description: "Distributed systems interviews",
icon: "network",
path: "/system-design",
},
];