leetcode-capture-helper 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/cli.js +92 -0
  2. package/config.js +25 -0
  3. package/framing.js +57 -0
  4. package/host.js +346 -0
  5. package/install.js +290 -0
  6. package/package.json +31 -4
  7. package/runtime/bundle.json +42 -0
  8. package/runtime/companion/capture-log.js +83 -0
  9. package/runtime/companion/codex-backend.js +337 -0
  10. package/runtime/companion/companion.js +2193 -0
  11. package/runtime/companion/mouse-input.js +154 -0
  12. package/runtime/companion/package.json +4 -0
  13. package/runtime/companion/panel-bridge.js +273 -0
  14. package/runtime/companion/settings-rpc.js +228 -0
  15. package/runtime/companion/settings.js +464 -0
  16. package/runtime/companion/terminal-format.js +330 -0
  17. package/runtime/companion/vault-summary.js +169 -0
  18. package/runtime/companion/virtual-terminal.js +138 -0
  19. package/runtime/extension/shared/anthropic-backend.js +193 -0
  20. package/runtime/extension/shared/catalog.js +415 -0
  21. package/runtime/extension/shared/code-ledger.js +333 -0
  22. package/runtime/extension/shared/context-budget.js +259 -0
  23. package/runtime/extension/shared/context-summary.js +271 -0
  24. package/runtime/extension/shared/conversation.js +81 -0
  25. package/runtime/extension/shared/event-hub.js +126 -0
  26. package/runtime/extension/shared/extension-settings.js +445 -0
  27. package/runtime/extension/shared/helper-launcher.js +248 -0
  28. package/runtime/extension/shared/local-backend.js +706 -0
  29. package/runtime/extension/shared/local-server.js +149 -0
  30. package/runtime/extension/shared/package.json +4 -0
  31. package/runtime/extension/shared/practice.js +228 -0
  32. package/runtime/extension/shared/problem-index.js +4128 -0
  33. package/runtime/extension/shared/profiles.js +106 -0
  34. package/runtime/extension/shared/providers.js +58 -0
  35. package/runtime/extension/shared/reply-guard.js +864 -0
  36. package/runtime/extension/shared/roadmap.js +1344 -0
  37. package/runtime/extension/shared/sanitize.js +68 -0
  38. package/runtime/extension/shared/solved.js +277 -0
  39. package/runtime/extension/shared/tutor-engine.js +369 -0
  40. package/runtime/extension/shared/tutor-prompt.js +108 -0
  41. package/runtime/extension/shared/tutor-service.js +520 -0
  42. package/runtime/relay-server/package.json +4 -0
  43. package/runtime/relay-server/panel-broker.js +797 -0
  44. package/runtime/relay-server/server.js +241 -0
  45. package/runtime/vault-tool/log-session.js +230 -0
  46. package/runtime/vault-tool/package.json +4 -0
  47. package/runtime/vault-tool/vault-notes.js +206 -0
  48. package/README.md +0 -3
@@ -0,0 +1,1344 @@
1
+ // The problem lists the Progress tab and the next-problem suggestions follow,
2
+ // plus the pure logic they share. No Node or browser APIs, so the options
3
+ // page, the background worker and the companion can all import it.
4
+ //
5
+ // A list is { id, title, short, description, kind, topics, edges, subjects }:
6
+ // topics its groups in the publisher's order, each { id, title, problems }
7
+ // where a problem is { slug, title, difficulty, topic } (title and
8
+ // difficulty come from the one shared catalog, shared/catalog.js)
9
+ // edges prerequisite lines [from, to] between topics; only NeetCode 150
10
+ // and NeetCode 250 have them (kind 'graph', one roadmap for both),
11
+ // every other list is drawn as ordered groups (kind 'groups') and
12
+ // its next group is simply the first one not finished
13
+ // subjects only for a list whose own groups are not subjects (Grind 75's
14
+ // weeks): a second grouping of the same problems by subject, each
15
+ // { id, title, problems } with the problems in the list's order;
16
+ // every problem then also carries `subject`. The list's order (and
17
+ // so what comes next) stays the publisher's; subjects are a view.
18
+ // The slug is LeetCode's own (leetcode.com/problems/<slug>/), which is also
19
+ // what the extension reads from the page address, so a problem solved once
20
+ // counts in every list.
21
+
22
+ import { CATALOG } from './catalog.js';
23
+
24
+ const words = (slugs) => slugs.split(/\s+/).filter(Boolean);
25
+
26
+ // Each group: [id, title, slugs], the slugs one per line in the list's order.
27
+ function defineList({ id, title, short, description, groupNoun, groups, edges = [], subjects = null }) {
28
+ const topics = groups.map(([topicId, topicTitle, slugs]) => ({
29
+ id: topicId,
30
+ title: topicTitle,
31
+ problems: words(slugs).map((slug) => {
32
+ const entry = CATALOG.get(slug);
33
+ if (!entry) throw new Error(`${id}: ${slug} is not in the catalog`);
34
+ return { slug, title: entry.title, difficulty: entry.difficulty, topic: topicId };
35
+ }),
36
+ }));
37
+ const size = topics.reduce((n, t) => n + t.problems.length, 0);
38
+ const list = { id, title, short: short || title, description, groupNoun, kind: edges.length ? 'graph' : 'groups', size, topics, edges, subjects: null };
39
+ if (subjects) list.subjects = defineSubjects(list, subjects);
40
+ return list;
41
+ }
42
+
43
+ // Every problem of the list in exactly one subject; each subject keeps the
44
+ // list's order.
45
+ function defineSubjects(list, subjects) {
46
+ const problems = list.topics.flatMap((t) => t.problems);
47
+ const where = new Map();
48
+ for (const [subjectId, , slugs] of subjects) {
49
+ for (const slug of words(slugs)) {
50
+ if (where.has(slug)) throw new Error(`${list.id}: ${slug} has two subjects`);
51
+ where.set(slug, subjectId);
52
+ }
53
+ }
54
+ for (const p of problems) {
55
+ if (!where.has(p.slug)) throw new Error(`${list.id}: ${p.slug} has no subject`);
56
+ p.subject = where.get(p.slug);
57
+ }
58
+ if (where.size !== problems.length) throw new Error(`${list.id}: a subject names a problem the list does not have`);
59
+ return subjects.map(([subjectId, subjectTitle]) => ({ id: subjectId, title: subjectTitle, problems: problems.filter((p) => p.subject === subjectId) }));
60
+ }
61
+
62
+ // NeetCode 150, in NeetCode's order, with its roadmap's prerequisite lines.
63
+ // Membership, topics and the order inside each topic are neetcode.io's own
64
+ // list data (https://neetcode.io/practice/practice/neetcode150, the problem
65
+ // objects in its main bundle, fetched 2026-11-04: e2e/neetcode-lists.txt,
66
+ // which roadmap.test.mjs compares). The older copy in the neetcode-gh/leetcode
67
+ // repository (.problemSiteData.json) still has Generate Parentheses under
68
+ // Stack; the site now lists it under Backtracking.
69
+ // The roadmap's own prerequisite lines: [from, to].
70
+ const EDGES = [
71
+ ['arrays-hashing', 'two-pointers'],
72
+ ['arrays-hashing', 'stack'],
73
+ ['two-pointers', 'binary-search'],
74
+ ['two-pointers', 'sliding-window'],
75
+ ['two-pointers', 'linked-list'],
76
+ ['binary-search', 'trees'],
77
+ ['linked-list', 'trees'],
78
+ ['trees', 'tries'],
79
+ ['trees', 'heap'],
80
+ ['trees', 'backtracking'],
81
+ ['backtracking', 'graphs'],
82
+ ['backtracking', 'dp-1d'],
83
+ ['heap', 'intervals'],
84
+ ['heap', 'greedy'],
85
+ ['heap', 'advanced-graphs'],
86
+ ['graphs', 'advanced-graphs'],
87
+ ['graphs', 'dp-2d'],
88
+ ['dp-1d', 'dp-2d'],
89
+ ['dp-1d', 'bit-manipulation'],
90
+ ['dp-2d', 'math-geometry'],
91
+ ['bit-manipulation', 'math-geometry'],
92
+ ];
93
+
94
+ export const NEETCODE_150 = defineList({
95
+ id: 'neetcode-150',
96
+ title: 'NeetCode 150',
97
+ description: 'NeetCode\'s expanded Blind 75, by topic on a roadmap.',
98
+ groupNoun: 'topic',
99
+ edges: EDGES,
100
+ groups: [
101
+ ['arrays-hashing', 'Arrays & Hashing', `
102
+ contains-duplicate
103
+ valid-anagram
104
+ two-sum
105
+ group-anagrams
106
+ top-k-frequent-elements
107
+ encode-and-decode-strings
108
+ product-of-array-except-self
109
+ valid-sudoku
110
+ longest-consecutive-sequence`],
111
+ ['two-pointers', 'Two Pointers', `
112
+ valid-palindrome
113
+ two-sum-ii-input-array-is-sorted
114
+ 3sum
115
+ container-with-most-water
116
+ trapping-rain-water`],
117
+ ['sliding-window', 'Sliding Window', `
118
+ best-time-to-buy-and-sell-stock
119
+ longest-substring-without-repeating-characters
120
+ longest-repeating-character-replacement
121
+ permutation-in-string
122
+ minimum-window-substring
123
+ sliding-window-maximum`],
124
+ ['stack', 'Stack', `
125
+ valid-parentheses
126
+ min-stack
127
+ evaluate-reverse-polish-notation
128
+ daily-temperatures
129
+ car-fleet
130
+ largest-rectangle-in-histogram`],
131
+ ['binary-search', 'Binary Search', `
132
+ binary-search
133
+ search-a-2d-matrix
134
+ koko-eating-bananas
135
+ find-minimum-in-rotated-sorted-array
136
+ search-in-rotated-sorted-array
137
+ time-based-key-value-store
138
+ median-of-two-sorted-arrays`],
139
+ ['linked-list', 'Linked List', `
140
+ reverse-linked-list
141
+ merge-two-sorted-lists
142
+ linked-list-cycle
143
+ reorder-list
144
+ remove-nth-node-from-end-of-list
145
+ copy-list-with-random-pointer
146
+ add-two-numbers
147
+ find-the-duplicate-number
148
+ lru-cache
149
+ merge-k-sorted-lists
150
+ reverse-nodes-in-k-group`],
151
+ ['trees', 'Trees', `
152
+ invert-binary-tree
153
+ maximum-depth-of-binary-tree
154
+ diameter-of-binary-tree
155
+ balanced-binary-tree
156
+ same-tree
157
+ subtree-of-another-tree
158
+ lowest-common-ancestor-of-a-binary-search-tree
159
+ binary-tree-level-order-traversal
160
+ binary-tree-right-side-view
161
+ count-good-nodes-in-binary-tree
162
+ validate-binary-search-tree
163
+ kth-smallest-element-in-a-bst
164
+ construct-binary-tree-from-preorder-and-inorder-traversal
165
+ binary-tree-maximum-path-sum
166
+ serialize-and-deserialize-binary-tree`],
167
+ ['heap', 'Heap / Priority Queue', `
168
+ kth-largest-element-in-a-stream
169
+ last-stone-weight
170
+ k-closest-points-to-origin
171
+ kth-largest-element-in-an-array
172
+ task-scheduler
173
+ design-twitter
174
+ find-median-from-data-stream`],
175
+ ['backtracking', 'Backtracking', `
176
+ subsets
177
+ combination-sum
178
+ combination-sum-ii
179
+ permutations
180
+ subsets-ii
181
+ generate-parentheses
182
+ word-search
183
+ palindrome-partitioning
184
+ letter-combinations-of-a-phone-number
185
+ n-queens`],
186
+ ['tries', 'Tries', `
187
+ implement-trie-prefix-tree
188
+ design-add-and-search-words-data-structure
189
+ word-search-ii`],
190
+ ['graphs', 'Graphs', `
191
+ number-of-islands
192
+ max-area-of-island
193
+ clone-graph
194
+ walls-and-gates
195
+ rotting-oranges
196
+ pacific-atlantic-water-flow
197
+ surrounded-regions
198
+ course-schedule
199
+ course-schedule-ii
200
+ graph-valid-tree
201
+ number-of-connected-components-in-an-undirected-graph
202
+ redundant-connection
203
+ word-ladder`],
204
+ ['advanced-graphs', 'Advanced Graphs', `
205
+ network-delay-time
206
+ reconstruct-itinerary
207
+ min-cost-to-connect-all-points
208
+ swim-in-rising-water
209
+ alien-dictionary
210
+ cheapest-flights-within-k-stops`],
211
+ ['dp-1d', '1-D Dynamic Programming', `
212
+ climbing-stairs
213
+ min-cost-climbing-stairs
214
+ house-robber
215
+ house-robber-ii
216
+ longest-palindromic-substring
217
+ palindromic-substrings
218
+ decode-ways
219
+ coin-change
220
+ maximum-product-subarray
221
+ word-break
222
+ longest-increasing-subsequence
223
+ partition-equal-subset-sum`],
224
+ ['dp-2d', '2-D Dynamic Programming', `
225
+ unique-paths
226
+ longest-common-subsequence
227
+ best-time-to-buy-and-sell-stock-with-cooldown
228
+ coin-change-ii
229
+ target-sum
230
+ interleaving-string
231
+ longest-increasing-path-in-a-matrix
232
+ distinct-subsequences
233
+ edit-distance
234
+ burst-balloons
235
+ regular-expression-matching`],
236
+ ['greedy', 'Greedy', `
237
+ maximum-subarray
238
+ jump-game
239
+ jump-game-ii
240
+ gas-station
241
+ hand-of-straights
242
+ merge-triplets-to-form-target-triplet
243
+ partition-labels
244
+ valid-parenthesis-string`],
245
+ ['intervals', 'Intervals', `
246
+ insert-interval
247
+ merge-intervals
248
+ non-overlapping-intervals
249
+ meeting-rooms
250
+ meeting-rooms-ii
251
+ minimum-interval-to-include-each-query`],
252
+ ['bit-manipulation', 'Bit Manipulation', `
253
+ single-number
254
+ number-of-1-bits
255
+ counting-bits
256
+ reverse-bits
257
+ missing-number
258
+ sum-of-two-integers
259
+ reverse-integer`],
260
+ ['math-geometry', 'Math & Geometry', `
261
+ rotate-image
262
+ spiral-matrix
263
+ set-matrix-zeroes
264
+ happy-number
265
+ plus-one
266
+ powx-n
267
+ multiply-strings
268
+ detect-squares`],
269
+ ],
270
+ });
271
+
272
+ // NeetCode 250: NeetCode 150 plus 100 more, on the same roadmap (the same
273
+ // topics and prerequisite lines). Membership and the order inside each topic
274
+ // are neetcode.io's own list data (https://neetcode.io/practice/practice/neetcode250,
275
+ // the problem objects in its main bundle, fetched 2026-11-04:
276
+ // e2e/neetcode-lists.txt, which roadmap.test.mjs compares). The topics keep
277
+ // NeetCode 150's order (neetcode.io puts Math & Geometry before Bit
278
+ // Manipulation here, but the roadmap leads from Bit Manipulation to it).
279
+ export const NEETCODE_250 = defineList({
280
+ id: 'neetcode-250',
281
+ title: 'NeetCode 250',
282
+ description: 'NeetCode 150 and 100 more, by topic on the same roadmap.',
283
+ groupNoun: 'topic',
284
+ edges: EDGES,
285
+ groups: [
286
+ ['arrays-hashing', 'Arrays & Hashing', `
287
+ concatenation-of-array
288
+ contains-duplicate
289
+ valid-anagram
290
+ two-sum
291
+ longest-common-prefix
292
+ group-anagrams
293
+ remove-element
294
+ majority-element
295
+ design-hashset
296
+ design-hashmap
297
+ sort-an-array
298
+ sort-colors
299
+ top-k-frequent-elements
300
+ encode-and-decode-strings
301
+ range-sum-query-2d-immutable
302
+ product-of-array-except-self
303
+ valid-sudoku
304
+ longest-consecutive-sequence
305
+ best-time-to-buy-and-sell-stock-ii
306
+ majority-element-ii
307
+ subarray-sum-equals-k
308
+ first-missing-positive`],
309
+ ['two-pointers', 'Two Pointers', `
310
+ reverse-string
311
+ valid-palindrome
312
+ valid-palindrome-ii
313
+ merge-strings-alternately
314
+ merge-sorted-array
315
+ remove-duplicates-from-sorted-array
316
+ two-sum-ii-input-array-is-sorted
317
+ 3sum
318
+ 4sum
319
+ rotate-array
320
+ container-with-most-water
321
+ boats-to-save-people
322
+ trapping-rain-water`],
323
+ ['sliding-window', 'Sliding Window', `
324
+ contains-duplicate-ii
325
+ best-time-to-buy-and-sell-stock
326
+ longest-substring-without-repeating-characters
327
+ longest-repeating-character-replacement
328
+ permutation-in-string
329
+ minimum-size-subarray-sum
330
+ find-k-closest-elements
331
+ minimum-window-substring
332
+ sliding-window-maximum`],
333
+ ['stack', 'Stack', `
334
+ baseball-game
335
+ valid-parentheses
336
+ implement-stack-using-queues
337
+ implement-queue-using-stacks
338
+ min-stack
339
+ evaluate-reverse-polish-notation
340
+ asteroid-collision
341
+ daily-temperatures
342
+ online-stock-span
343
+ car-fleet
344
+ simplify-path
345
+ decode-string
346
+ maximum-frequency-stack
347
+ largest-rectangle-in-histogram`],
348
+ ['binary-search', 'Binary Search', `
349
+ binary-search
350
+ search-insert-position
351
+ guess-number-higher-or-lower
352
+ sqrtx
353
+ search-a-2d-matrix
354
+ koko-eating-bananas
355
+ capacity-to-ship-packages-within-d-days
356
+ find-minimum-in-rotated-sorted-array
357
+ search-in-rotated-sorted-array
358
+ search-in-rotated-sorted-array-ii
359
+ time-based-key-value-store
360
+ split-array-largest-sum
361
+ median-of-two-sorted-arrays
362
+ find-in-mountain-array`],
363
+ ['linked-list', 'Linked List', `
364
+ reverse-linked-list
365
+ merge-two-sorted-lists
366
+ linked-list-cycle
367
+ reorder-list
368
+ remove-nth-node-from-end-of-list
369
+ copy-list-with-random-pointer
370
+ add-two-numbers
371
+ find-the-duplicate-number
372
+ reverse-linked-list-ii
373
+ design-circular-queue
374
+ lru-cache
375
+ lfu-cache
376
+ merge-k-sorted-lists
377
+ reverse-nodes-in-k-group`],
378
+ ['trees', 'Trees', `
379
+ binary-tree-inorder-traversal
380
+ binary-tree-preorder-traversal
381
+ binary-tree-postorder-traversal
382
+ invert-binary-tree
383
+ maximum-depth-of-binary-tree
384
+ diameter-of-binary-tree
385
+ balanced-binary-tree
386
+ same-tree
387
+ subtree-of-another-tree
388
+ lowest-common-ancestor-of-a-binary-search-tree
389
+ insert-into-a-binary-search-tree
390
+ delete-node-in-a-bst
391
+ binary-tree-level-order-traversal
392
+ binary-tree-right-side-view
393
+ construct-quad-tree
394
+ count-good-nodes-in-binary-tree
395
+ validate-binary-search-tree
396
+ kth-smallest-element-in-a-bst
397
+ construct-binary-tree-from-preorder-and-inorder-traversal
398
+ house-robber-iii
399
+ delete-leaves-with-a-given-value
400
+ binary-tree-maximum-path-sum
401
+ serialize-and-deserialize-binary-tree`],
402
+ ['heap', 'Heap / Priority Queue', `
403
+ kth-largest-element-in-a-stream
404
+ last-stone-weight
405
+ k-closest-points-to-origin
406
+ kth-largest-element-in-an-array
407
+ task-scheduler
408
+ design-twitter
409
+ single-threaded-cpu
410
+ reorganize-string
411
+ longest-happy-string
412
+ car-pooling
413
+ find-median-from-data-stream
414
+ ipo`],
415
+ ['backtracking', 'Backtracking', `
416
+ sum-of-all-subset-xor-totals
417
+ subsets
418
+ combination-sum
419
+ combination-sum-ii
420
+ combinations
421
+ permutations
422
+ subsets-ii
423
+ permutations-ii
424
+ generate-parentheses
425
+ word-search
426
+ palindrome-partitioning
427
+ letter-combinations-of-a-phone-number
428
+ matchsticks-to-square
429
+ partition-to-k-equal-sum-subsets
430
+ n-queens
431
+ n-queens-ii
432
+ word-break-ii`],
433
+ ['tries', 'Tries', `
434
+ implement-trie-prefix-tree
435
+ design-add-and-search-words-data-structure
436
+ extra-characters-in-a-string
437
+ word-search-ii`],
438
+ ['graphs', 'Graphs', `
439
+ island-perimeter
440
+ verifying-an-alien-dictionary
441
+ find-the-town-judge
442
+ number-of-islands
443
+ max-area-of-island
444
+ clone-graph
445
+ walls-and-gates
446
+ rotting-oranges
447
+ pacific-atlantic-water-flow
448
+ surrounded-regions
449
+ open-the-lock
450
+ course-schedule
451
+ course-schedule-ii
452
+ graph-valid-tree
453
+ course-schedule-iv
454
+ number-of-connected-components-in-an-undirected-graph
455
+ redundant-connection
456
+ accounts-merge
457
+ evaluate-division
458
+ minimum-height-trees
459
+ word-ladder`],
460
+ ['advanced-graphs', 'Advanced Graphs', `
461
+ path-with-minimum-effort
462
+ network-delay-time
463
+ reconstruct-itinerary
464
+ min-cost-to-connect-all-points
465
+ swim-in-rising-water
466
+ alien-dictionary
467
+ cheapest-flights-within-k-stops
468
+ find-critical-and-pseudo-critical-edges-in-minimum-spanning-tree
469
+ build-a-matrix-with-conditions
470
+ greatest-common-divisor-traversal`],
471
+ ['dp-1d', '1-D Dynamic Programming', `
472
+ climbing-stairs
473
+ min-cost-climbing-stairs
474
+ n-th-tribonacci-number
475
+ house-robber
476
+ house-robber-ii
477
+ longest-palindromic-substring
478
+ palindromic-substrings
479
+ decode-ways
480
+ coin-change
481
+ maximum-product-subarray
482
+ word-break
483
+ longest-increasing-subsequence
484
+ partition-equal-subset-sum
485
+ combination-sum-iv
486
+ perfect-squares
487
+ integer-break
488
+ stone-game-iii`],
489
+ ['dp-2d', '2-D Dynamic Programming', `
490
+ unique-paths
491
+ unique-paths-ii
492
+ minimum-path-sum
493
+ longest-common-subsequence
494
+ last-stone-weight-ii
495
+ best-time-to-buy-and-sell-stock-with-cooldown
496
+ coin-change-ii
497
+ target-sum
498
+ interleaving-string
499
+ stone-game
500
+ stone-game-ii
501
+ longest-increasing-path-in-a-matrix
502
+ distinct-subsequences
503
+ edit-distance
504
+ burst-balloons
505
+ regular-expression-matching`],
506
+ ['greedy', 'Greedy', `
507
+ lemonade-change
508
+ maximum-subarray
509
+ maximum-sum-circular-subarray
510
+ longest-turbulent-subarray
511
+ jump-game
512
+ jump-game-ii
513
+ jump-game-vii
514
+ gas-station
515
+ hand-of-straights
516
+ dota2-senate
517
+ merge-triplets-to-form-target-triplet
518
+ partition-labels
519
+ valid-parenthesis-string
520
+ candy`],
521
+ ['intervals', 'Intervals', `
522
+ insert-interval
523
+ merge-intervals
524
+ non-overlapping-intervals
525
+ meeting-rooms
526
+ meeting-rooms-ii
527
+ meeting-rooms-iii
528
+ minimum-interval-to-include-each-query`],
529
+ ['bit-manipulation', 'Bit Manipulation', `
530
+ single-number
531
+ number-of-1-bits
532
+ counting-bits
533
+ add-binary
534
+ reverse-bits
535
+ missing-number
536
+ sum-of-two-integers
537
+ reverse-integer
538
+ bitwise-and-of-numbers-range
539
+ minimum-array-end`],
540
+ ['math-geometry', 'Math & Geometry', `
541
+ excel-sheet-column-title
542
+ greatest-common-divisor-of-strings
543
+ insert-greatest-common-divisors-in-linked-list
544
+ transpose-matrix
545
+ rotate-image
546
+ spiral-matrix
547
+ set-matrix-zeroes
548
+ happy-number
549
+ plus-one
550
+ roman-to-integer
551
+ powx-n
552
+ multiply-strings
553
+ detect-squares`],
554
+ ],
555
+ });
556
+
557
+ // The original Blind 75, exactly as the 2018 post lists it
558
+ // (https://www.teamblind.com/post/New-Year-Gift---Curated-List-of-Top-75-LeetCode-Questions-to-Save-Your-Time-OaM1orEU,
559
+ // saved as e2e/blind-75.txt by scripts/list-sources.mjs and compared by
560
+ // roadmap.test.mjs): its categories, their order and the order inside each.
561
+ // Merge k Sorted Lists is listed under both Linked List and Heap there; it is
562
+ // kept once, under Linked List, so the list has 75 distinct problems.
563
+ // neetcode.io's Blind 75 (its blind75 flag) has the same problems but one:
564
+ // the post's "Combination Sum" links to Combination Sum IV, which neetcode.io
565
+ // replaces with Combination Sum. The post's own link is kept here.
566
+ export const BLIND_75 = defineList({
567
+ id: 'blind-75',
568
+ title: 'Blind 75',
569
+ description: 'The original 75 from Blind, by category. NeetCode 150 extends it.',
570
+ groupNoun: 'category',
571
+ groups: [
572
+ ['array', 'Array', `
573
+ two-sum
574
+ best-time-to-buy-and-sell-stock
575
+ contains-duplicate
576
+ product-of-array-except-self
577
+ maximum-subarray
578
+ maximum-product-subarray
579
+ find-minimum-in-rotated-sorted-array
580
+ search-in-rotated-sorted-array
581
+ 3sum
582
+ container-with-most-water`],
583
+ ['binary', 'Binary', `
584
+ sum-of-two-integers
585
+ number-of-1-bits
586
+ counting-bits
587
+ missing-number
588
+ reverse-bits`],
589
+ ['dynamic-programming', 'Dynamic Programming', `
590
+ climbing-stairs
591
+ coin-change
592
+ longest-increasing-subsequence
593
+ longest-common-subsequence
594
+ word-break
595
+ combination-sum-iv
596
+ house-robber
597
+ house-robber-ii
598
+ decode-ways
599
+ unique-paths
600
+ jump-game`],
601
+ ['graph', 'Graph', `
602
+ clone-graph
603
+ course-schedule
604
+ pacific-atlantic-water-flow
605
+ number-of-islands
606
+ longest-consecutive-sequence
607
+ alien-dictionary
608
+ graph-valid-tree
609
+ number-of-connected-components-in-an-undirected-graph`],
610
+ ['interval', 'Interval', `
611
+ insert-interval
612
+ merge-intervals
613
+ non-overlapping-intervals
614
+ meeting-rooms
615
+ meeting-rooms-ii`],
616
+ ['linked-list', 'Linked List', `
617
+ reverse-linked-list
618
+ linked-list-cycle
619
+ merge-two-sorted-lists
620
+ merge-k-sorted-lists
621
+ remove-nth-node-from-end-of-list
622
+ reorder-list`],
623
+ ['matrix', 'Matrix', `
624
+ set-matrix-zeroes
625
+ spiral-matrix
626
+ rotate-image
627
+ word-search`],
628
+ ['string', 'String', `
629
+ longest-substring-without-repeating-characters
630
+ longest-repeating-character-replacement
631
+ minimum-window-substring
632
+ valid-anagram
633
+ group-anagrams
634
+ valid-parentheses
635
+ valid-palindrome
636
+ longest-palindromic-substring
637
+ palindromic-substrings
638
+ encode-and-decode-strings`],
639
+ ['tree', 'Tree', `
640
+ maximum-depth-of-binary-tree
641
+ same-tree
642
+ invert-binary-tree
643
+ binary-tree-maximum-path-sum
644
+ binary-tree-level-order-traversal
645
+ serialize-and-deserialize-binary-tree
646
+ subtree-of-another-tree
647
+ construct-binary-tree-from-preorder-and-inorder-traversal
648
+ validate-binary-search-tree
649
+ kth-smallest-element-in-a-bst
650
+ lowest-common-ancestor-of-a-binary-search-tree
651
+ implement-trie-prefix-tree
652
+ design-add-and-search-words-data-structure
653
+ word-search-ii`],
654
+ ['heap', 'Heap', `
655
+ top-k-frequent-elements
656
+ find-median-from-data-stream`],
657
+ ],
658
+ });
659
+
660
+ // Grind 75 (Yangshun Tay, https://www.techinterviewhandbook.org/grind75) as
661
+ // the site shows it at its defaults: 8 weeks at 8 hours a week, ordered by
662
+ // difficulty, grouped by weeks. The site computes that from its own data (169
663
+ // questions): it takes questions by priority while 1.96 minutes per minute of
664
+ // each one's duration fit in 60 * 8 * 8, which gives exactly these 75, orders
665
+ // them Easy, Medium, Hard (keeping priority order inside each), and starts a
666
+ // new week once the next one would pass 60 * 8. Other settings change both
667
+ // the set and the weeks (12 weeks gives 111 questions), so this list is only
668
+ // the default. e2e/grind-75.txt is that result, written by
669
+ // scripts/list-sources.mjs, and roadmap.test.mjs compares it. The site still
670
+ // grades Lowest Common Ancestor of a Binary Search Tree Easy; LeetCode now
671
+ // says Medium, and the catalog keeps LeetCode's grade.
672
+ export const GRIND_75 = defineList({
673
+ id: 'grind-75',
674
+ title: 'Grind 75',
675
+ description: 'By the Blind 75 author: the site\'s default eight weeks, or by subject.',
676
+ groupNoun: 'week',
677
+ groups: [
678
+ ['week-1', 'Week 1', `
679
+ two-sum
680
+ valid-parentheses
681
+ merge-two-sorted-lists
682
+ best-time-to-buy-and-sell-stock
683
+ valid-palindrome
684
+ invert-binary-tree
685
+ valid-anagram
686
+ binary-search
687
+ flood-fill
688
+ lowest-common-ancestor-of-a-binary-search-tree
689
+ balanced-binary-tree
690
+ linked-list-cycle
691
+ implement-queue-using-stacks`],
692
+ ['week-2', 'Week 2', `
693
+ first-bad-version
694
+ ransom-note
695
+ climbing-stairs
696
+ longest-palindrome
697
+ reverse-linked-list
698
+ majority-element
699
+ add-binary
700
+ diameter-of-binary-tree
701
+ middle-of-the-linked-list
702
+ maximum-depth-of-binary-tree
703
+ contains-duplicate
704
+ maximum-subarray`],
705
+ ['week-3', 'Week 3', `
706
+ insert-interval
707
+ 01-matrix
708
+ k-closest-points-to-origin
709
+ longest-substring-without-repeating-characters
710
+ 3sum
711
+ binary-tree-level-order-traversal
712
+ clone-graph
713
+ evaluate-reverse-polish-notation`],
714
+ ['week-4', 'Week 4', `
715
+ course-schedule
716
+ implement-trie-prefix-tree
717
+ coin-change
718
+ product-of-array-except-self
719
+ min-stack
720
+ validate-binary-search-tree
721
+ number-of-islands
722
+ rotting-oranges`],
723
+ ['week-5', 'Week 5', `
724
+ search-in-rotated-sorted-array
725
+ combination-sum
726
+ permutations
727
+ merge-intervals
728
+ lowest-common-ancestor-of-a-binary-tree
729
+ time-based-key-value-store
730
+ accounts-merge
731
+ sort-colors`],
732
+ ['week-6', 'Week 6', `
733
+ word-break
734
+ partition-equal-subset-sum
735
+ string-to-integer-atoi
736
+ spiral-matrix
737
+ subsets
738
+ binary-tree-right-side-view
739
+ longest-palindromic-substring
740
+ unique-paths
741
+ construct-binary-tree-from-preorder-and-inorder-traversal`],
742
+ ['week-7', 'Week 7', `
743
+ container-with-most-water
744
+ letter-combinations-of-a-phone-number
745
+ word-search
746
+ find-all-anagrams-in-a-string
747
+ minimum-height-trees
748
+ task-scheduler
749
+ lru-cache`],
750
+ ['week-8', 'Week 8', `
751
+ kth-smallest-element-in-a-bst
752
+ minimum-window-substring
753
+ serialize-and-deserialize-binary-tree
754
+ trapping-rain-water
755
+ find-median-from-data-stream
756
+ word-ladder
757
+ basic-calculator
758
+ maximum-profit-in-job-scheduling
759
+ merge-k-sorted-lists
760
+ largest-rectangle-in-histogram`],
761
+ ],
762
+ // Grind 75 groups by week only, so each problem also gets one subject, in
763
+ // NeetCode's topic names and order: the topic neetcode.io itself gives the
764
+ // problem (its NeetCode 150 topic, else its NeetCode 250 topic, else the
765
+ // topic in neetcode.io's full catalog; e2e/neetcode-lists.txt has all three,
766
+ // and roadmap.test.mjs checks the rule). Four are not on neetcode.io at all
767
+ // and follow their LeetCode tags (shared/problem-index.js): 01 Matrix (Array,
768
+ // Dynamic Programming, Matrix, Breadth-First Search) is Graphs, a grid BFS
769
+ // like Rotting Oranges; First Bad Version (Binary Search, Interactive) is
770
+ // Binary Search; String to Integer (atoi) (String only) is Arrays & Hashing,
771
+ // where NeetCode puts plain string problems; Basic Calculator (String, Math,
772
+ // Stack, Recursion) is Stack.
773
+ subjects: [
774
+ ['arrays-hashing', 'Arrays & Hashing', `
775
+ two-sum
776
+ valid-anagram
777
+ ransom-note
778
+ longest-palindrome
779
+ majority-element
780
+ contains-duplicate
781
+ product-of-array-except-self
782
+ sort-colors
783
+ string-to-integer-atoi
784
+ find-all-anagrams-in-a-string`],
785
+ ['two-pointers', 'Two Pointers', `
786
+ valid-palindrome
787
+ 3sum
788
+ container-with-most-water
789
+ trapping-rain-water`],
790
+ ['sliding-window', 'Sliding Window', `
791
+ best-time-to-buy-and-sell-stock
792
+ longest-substring-without-repeating-characters
793
+ minimum-window-substring`],
794
+ ['stack', 'Stack', `
795
+ valid-parentheses
796
+ implement-queue-using-stacks
797
+ evaluate-reverse-polish-notation
798
+ min-stack
799
+ basic-calculator
800
+ largest-rectangle-in-histogram`],
801
+ ['binary-search', 'Binary Search', `
802
+ binary-search
803
+ first-bad-version
804
+ search-in-rotated-sorted-array
805
+ time-based-key-value-store`],
806
+ ['linked-list', 'Linked List', `
807
+ merge-two-sorted-lists
808
+ linked-list-cycle
809
+ reverse-linked-list
810
+ middle-of-the-linked-list
811
+ lru-cache
812
+ merge-k-sorted-lists`],
813
+ ['trees', 'Trees', `
814
+ invert-binary-tree
815
+ lowest-common-ancestor-of-a-binary-search-tree
816
+ balanced-binary-tree
817
+ diameter-of-binary-tree
818
+ maximum-depth-of-binary-tree
819
+ binary-tree-level-order-traversal
820
+ validate-binary-search-tree
821
+ lowest-common-ancestor-of-a-binary-tree
822
+ binary-tree-right-side-view
823
+ construct-binary-tree-from-preorder-and-inorder-traversal
824
+ kth-smallest-element-in-a-bst
825
+ serialize-and-deserialize-binary-tree`],
826
+ ['heap', 'Heap / Priority Queue', `
827
+ k-closest-points-to-origin
828
+ task-scheduler
829
+ find-median-from-data-stream`],
830
+ ['backtracking', 'Backtracking', `
831
+ combination-sum
832
+ permutations
833
+ subsets
834
+ letter-combinations-of-a-phone-number
835
+ word-search`],
836
+ ['tries', 'Tries', `
837
+ implement-trie-prefix-tree`],
838
+ ['graphs', 'Graphs', `
839
+ flood-fill
840
+ 01-matrix
841
+ clone-graph
842
+ course-schedule
843
+ number-of-islands
844
+ rotting-oranges
845
+ accounts-merge
846
+ minimum-height-trees
847
+ word-ladder`],
848
+ ['dp-1d', '1-D Dynamic Programming', `
849
+ climbing-stairs
850
+ coin-change
851
+ word-break
852
+ partition-equal-subset-sum
853
+ longest-palindromic-substring
854
+ maximum-profit-in-job-scheduling`],
855
+ ['dp-2d', '2-D Dynamic Programming', `
856
+ unique-paths`],
857
+ ['greedy', 'Greedy', `
858
+ maximum-subarray`],
859
+ ['intervals', 'Intervals', `
860
+ insert-interval
861
+ merge-intervals`],
862
+ ['bit-manipulation', 'Bit Manipulation', `
863
+ add-binary`],
864
+ ['math-geometry', 'Math & Geometry', `
865
+ spiral-matrix`],
866
+ ],
867
+ });
868
+
869
+ // LeetCode's own Top 100 Liked study plan (plan top-100-liked), with its
870
+ // groups exactly as LeetCode's GraphQL gave them on 2026-11-04 (so Binary Tree
871
+ // Maximum Path Sum sits under Binary Search, as LeetCode has it), checked
872
+ // against e2e/leetcode-study-plans.txt by roadmap.test.mjs.
873
+ export const TOP_100_LIKED = defineList({
874
+ id: 'top-100-liked',
875
+ title: 'Top 100 Liked',
876
+ description: 'LeetCode\'s 100 most liked problems, by topic.',
877
+ groupNoun: 'topic',
878
+ groups: [
879
+ ['backtracking', 'Backtracking', `
880
+ letter-combinations-of-a-phone-number
881
+ generate-parentheses
882
+ combination-sum
883
+ permutations
884
+ n-queens
885
+ subsets
886
+ word-search
887
+ palindrome-partitioning`],
888
+ ['binary-search', 'Binary Search', `
889
+ median-of-two-sorted-arrays
890
+ search-in-rotated-sorted-array
891
+ find-first-and-last-position-of-element-in-sorted-array
892
+ search-insert-position
893
+ search-a-2d-matrix
894
+ binary-tree-maximum-path-sum
895
+ find-minimum-in-rotated-sorted-array`],
896
+ ['binary-tree', 'Binary Tree', `
897
+ binary-tree-inorder-traversal
898
+ validate-binary-search-tree
899
+ symmetric-tree
900
+ binary-tree-level-order-traversal
901
+ maximum-depth-of-binary-tree
902
+ construct-binary-tree-from-preorder-and-inorder-traversal
903
+ convert-sorted-array-to-binary-search-tree
904
+ flatten-binary-tree-to-linked-list
905
+ binary-tree-right-side-view
906
+ invert-binary-tree
907
+ kth-smallest-element-in-a-bst
908
+ lowest-common-ancestor-of-a-binary-tree
909
+ path-sum-iii
910
+ diameter-of-binary-tree`],
911
+ ['dynamic-programming', 'Dynamic Programming', `
912
+ longest-palindromic-substring
913
+ longest-valid-parentheses
914
+ unique-paths
915
+ minimum-path-sum
916
+ climbing-stairs
917
+ edit-distance
918
+ pascals-triangle
919
+ word-break
920
+ maximum-product-subarray
921
+ house-robber
922
+ perfect-squares
923
+ longest-increasing-subsequence
924
+ coin-change
925
+ partition-equal-subset-sum
926
+ longest-common-subsequence`],
927
+ ['graph', 'Graph', `
928
+ number-of-islands
929
+ course-schedule
930
+ rotting-oranges`],
931
+ ['greedy', 'Greedy', `
932
+ jump-game-ii
933
+ jump-game
934
+ best-time-to-buy-and-sell-stock
935
+ partition-labels`],
936
+ ['hashing', 'Hashing', `
937
+ two-sum
938
+ group-anagrams
939
+ longest-consecutive-sequence
940
+ subarray-sum-equals-k`],
941
+ ['heap', 'Heap', `
942
+ kth-largest-element-in-an-array
943
+ find-median-from-data-stream
944
+ top-k-frequent-elements`],
945
+ ['linked-lists', 'Linked Lists', `
946
+ add-two-numbers
947
+ remove-nth-node-from-end-of-list
948
+ merge-two-sorted-lists
949
+ merge-k-sorted-lists
950
+ swap-nodes-in-pairs
951
+ reverse-nodes-in-k-group
952
+ copy-list-with-random-pointer
953
+ linked-list-cycle
954
+ linked-list-cycle-ii
955
+ lru-cache
956
+ sort-list
957
+ intersection-of-two-linked-lists
958
+ reverse-linked-list
959
+ palindrome-linked-list`],
960
+ ['matrix', 'Matrix', `
961
+ rotate-image
962
+ spiral-matrix
963
+ set-matrix-zeroes
964
+ search-a-2d-matrix-ii`],
965
+ ['sliding-window', 'Sliding Window', `
966
+ longest-substring-without-repeating-characters
967
+ minimum-window-substring
968
+ sliding-window-maximum
969
+ find-all-anagrams-in-a-string`],
970
+ ['stack', 'Stack', `
971
+ valid-parentheses
972
+ largest-rectangle-in-histogram
973
+ min-stack
974
+ decode-string
975
+ daily-temperatures`],
976
+ ['two-pointers', 'Two Pointers', `
977
+ container-with-most-water
978
+ 3sum
979
+ trapping-rain-water
980
+ move-zeroes`],
981
+ ['trie', 'Trie', `
982
+ implement-trie-prefix-tree`],
983
+ ['misc', 'Misc', `
984
+ next-permutation
985
+ first-missing-positive
986
+ maximum-subarray
987
+ merge-intervals
988
+ sort-colors
989
+ single-number
990
+ majority-element
991
+ rotate-array
992
+ product-of-array-except-self
993
+ find-the-duplicate-number`],
994
+ ],
995
+ });
996
+
997
+ // LeetCode's own Top Interview 150 study plan, with its sections, as
998
+ // LeetCode's GraphQL (studyPlanV2Detail, plan top-interview-150) gave them on
999
+ // 2026-11-04: e2e/leetcode-study-plans.txt, which roadmap.test.mjs compares.
1000
+ export const TOP_INTERVIEW_150 = defineList({
1001
+ id: 'top-interview-150',
1002
+ title: 'Top Interview 150',
1003
+ description: 'LeetCode\'s own interview study plan, section by section.',
1004
+ groupNoun: 'section',
1005
+ groups: [
1006
+ ['array-string', 'Array / String', `
1007
+ merge-sorted-array
1008
+ remove-element
1009
+ remove-duplicates-from-sorted-array
1010
+ remove-duplicates-from-sorted-array-ii
1011
+ majority-element
1012
+ rotate-array
1013
+ best-time-to-buy-and-sell-stock
1014
+ best-time-to-buy-and-sell-stock-ii
1015
+ jump-game
1016
+ jump-game-ii
1017
+ h-index
1018
+ insert-delete-getrandom-o1
1019
+ product-of-array-except-self
1020
+ gas-station
1021
+ candy
1022
+ trapping-rain-water
1023
+ roman-to-integer
1024
+ integer-to-roman
1025
+ length-of-last-word
1026
+ longest-common-prefix
1027
+ reverse-words-in-a-string
1028
+ zigzag-conversion
1029
+ find-the-index-of-the-first-occurrence-in-a-string
1030
+ text-justification`],
1031
+ ['two-pointers', 'Two Pointers', `
1032
+ valid-palindrome
1033
+ is-subsequence
1034
+ two-sum-ii-input-array-is-sorted
1035
+ container-with-most-water
1036
+ 3sum`],
1037
+ ['sliding-window', 'Sliding Window', `
1038
+ minimum-size-subarray-sum
1039
+ longest-substring-without-repeating-characters
1040
+ substring-with-concatenation-of-all-words
1041
+ minimum-window-substring`],
1042
+ ['matrix', 'Matrix', `
1043
+ valid-sudoku
1044
+ spiral-matrix
1045
+ rotate-image
1046
+ set-matrix-zeroes
1047
+ game-of-life`],
1048
+ ['hashmap', 'Hashmap', `
1049
+ ransom-note
1050
+ isomorphic-strings
1051
+ word-pattern
1052
+ valid-anagram
1053
+ group-anagrams
1054
+ two-sum
1055
+ happy-number
1056
+ contains-duplicate-ii
1057
+ longest-consecutive-sequence`],
1058
+ ['intervals', 'Intervals', `
1059
+ summary-ranges
1060
+ merge-intervals
1061
+ insert-interval
1062
+ minimum-number-of-arrows-to-burst-balloons`],
1063
+ ['stack', 'Stack', `
1064
+ valid-parentheses
1065
+ simplify-path
1066
+ min-stack
1067
+ evaluate-reverse-polish-notation
1068
+ basic-calculator`],
1069
+ ['linked-list', 'Linked List', `
1070
+ linked-list-cycle
1071
+ add-two-numbers
1072
+ merge-two-sorted-lists
1073
+ copy-list-with-random-pointer
1074
+ reverse-linked-list-ii
1075
+ reverse-nodes-in-k-group
1076
+ remove-nth-node-from-end-of-list
1077
+ remove-duplicates-from-sorted-list-ii
1078
+ rotate-list
1079
+ partition-list
1080
+ lru-cache`],
1081
+ ['binary-tree-general', 'Binary Tree General', `
1082
+ maximum-depth-of-binary-tree
1083
+ same-tree
1084
+ invert-binary-tree
1085
+ symmetric-tree
1086
+ construct-binary-tree-from-preorder-and-inorder-traversal
1087
+ construct-binary-tree-from-inorder-and-postorder-traversal
1088
+ populating-next-right-pointers-in-each-node-ii
1089
+ flatten-binary-tree-to-linked-list
1090
+ path-sum
1091
+ sum-root-to-leaf-numbers
1092
+ binary-tree-maximum-path-sum
1093
+ binary-search-tree-iterator
1094
+ count-complete-tree-nodes
1095
+ lowest-common-ancestor-of-a-binary-tree`],
1096
+ ['binary-tree-bfs', 'Binary Tree BFS', `
1097
+ binary-tree-right-side-view
1098
+ average-of-levels-in-binary-tree
1099
+ binary-tree-level-order-traversal
1100
+ binary-tree-zigzag-level-order-traversal`],
1101
+ ['binary-search-tree', 'Binary Search Tree', `
1102
+ minimum-absolute-difference-in-bst
1103
+ kth-smallest-element-in-a-bst
1104
+ validate-binary-search-tree`],
1105
+ ['graph-general', 'Graph General', `
1106
+ number-of-islands
1107
+ surrounded-regions
1108
+ clone-graph
1109
+ evaluate-division
1110
+ course-schedule
1111
+ course-schedule-ii`],
1112
+ ['graph-bfs', 'Graph BFS', `
1113
+ snakes-and-ladders
1114
+ minimum-genetic-mutation
1115
+ word-ladder`],
1116
+ ['trie', 'Trie', `
1117
+ implement-trie-prefix-tree
1118
+ design-add-and-search-words-data-structure
1119
+ word-search-ii`],
1120
+ ['backtracking', 'Backtracking', `
1121
+ letter-combinations-of-a-phone-number
1122
+ combinations
1123
+ permutations
1124
+ combination-sum
1125
+ n-queens-ii
1126
+ generate-parentheses
1127
+ word-search`],
1128
+ ['divide-conquer', 'Divide & Conquer', `
1129
+ convert-sorted-array-to-binary-search-tree
1130
+ sort-list
1131
+ construct-quad-tree
1132
+ merge-k-sorted-lists`],
1133
+ ['kadane', "Kadane's Algorithm", `
1134
+ maximum-subarray
1135
+ maximum-sum-circular-subarray`],
1136
+ ['binary-search', 'Binary Search', `
1137
+ search-insert-position
1138
+ search-a-2d-matrix
1139
+ find-peak-element
1140
+ search-in-rotated-sorted-array
1141
+ find-first-and-last-position-of-element-in-sorted-array
1142
+ find-minimum-in-rotated-sorted-array
1143
+ median-of-two-sorted-arrays`],
1144
+ ['heap', 'Heap', `
1145
+ kth-largest-element-in-an-array
1146
+ ipo
1147
+ find-k-pairs-with-smallest-sums
1148
+ find-median-from-data-stream`],
1149
+ ['bit-manipulation', 'Bit Manipulation', `
1150
+ add-binary
1151
+ reverse-bits
1152
+ number-of-1-bits
1153
+ single-number
1154
+ single-number-ii
1155
+ bitwise-and-of-numbers-range`],
1156
+ ['math', 'Math', `
1157
+ palindrome-number
1158
+ plus-one
1159
+ factorial-trailing-zeroes
1160
+ sqrtx
1161
+ powx-n
1162
+ max-points-on-a-line`],
1163
+ ['dp-1d', '1D DP', `
1164
+ climbing-stairs
1165
+ house-robber
1166
+ word-break
1167
+ coin-change
1168
+ longest-increasing-subsequence`],
1169
+ ['dp-multi', 'Multidimensional DP', `
1170
+ triangle
1171
+ minimum-path-sum
1172
+ unique-paths-ii
1173
+ longest-palindromic-substring
1174
+ interleaving-string
1175
+ edit-distance
1176
+ best-time-to-buy-and-sell-stock-iii
1177
+ best-time-to-buy-and-sell-stock-iv
1178
+ maximal-square`],
1179
+ ],
1180
+ });
1181
+
1182
+ // LeetCode's own SQL 50 study plan (database problems, plan top-sql-50), with
1183
+ // its sections, checked against e2e/leetcode-study-plans.txt the same way.
1184
+ export const SQL_50 = defineList({
1185
+ id: 'sql-50',
1186
+ title: 'SQL 50',
1187
+ description: 'LeetCode\'s study plan for database problems.',
1188
+ groupNoun: 'section',
1189
+ groups: [
1190
+ ['select', 'Select', `
1191
+ recyclable-and-low-fat-products
1192
+ find-customer-referee
1193
+ big-countries
1194
+ article-views-i
1195
+ invalid-tweets`],
1196
+ ['basic-joins', 'Basic Joins', `
1197
+ replace-employee-id-with-the-unique-identifier
1198
+ product-sales-analysis-i
1199
+ customer-who-visited-but-did-not-make-any-transactions
1200
+ rising-temperature
1201
+ average-time-of-process-per-machine
1202
+ employee-bonus
1203
+ students-and-examinations
1204
+ managers-with-at-least-5-direct-reports
1205
+ confirmation-rate`],
1206
+ ['basic-aggregate', 'Basic Aggregate Functions', `
1207
+ not-boring-movies
1208
+ average-selling-price
1209
+ project-employees-i
1210
+ percentage-of-users-attended-a-contest
1211
+ queries-quality-and-percentage
1212
+ monthly-transactions-i
1213
+ immediate-food-delivery-ii
1214
+ game-play-analysis-iv`],
1215
+ ['sorting-grouping', 'Sorting and Grouping', `
1216
+ number-of-unique-subjects-taught-by-each-teacher
1217
+ user-activity-for-the-past-30-days-i
1218
+ product-sales-analysis-iii
1219
+ classes-with-at-least-5-students
1220
+ find-followers-count
1221
+ biggest-single-number
1222
+ customers-who-bought-all-products`],
1223
+ ['advanced-select-joins', 'Advanced Select and Joins', `
1224
+ the-number-of-employees-which-report-to-each-employee
1225
+ primary-department-for-each-employee
1226
+ triangle-judgement
1227
+ consecutive-numbers
1228
+ product-price-at-a-given-date
1229
+ last-person-to-fit-in-the-bus
1230
+ count-salary-categories`],
1231
+ ['subqueries', 'Subqueries', `
1232
+ employees-whose-manager-left-the-company
1233
+ exchange-seats
1234
+ movie-rating
1235
+ restaurant-growth
1236
+ friend-requests-ii-who-has-the-most-friends
1237
+ investments-in-2016
1238
+ department-top-three-salaries`],
1239
+ ['advanced-string', 'Advanced String Functions / Regex / Clause', `
1240
+ fix-names-in-a-table
1241
+ patients-with-a-condition
1242
+ delete-duplicate-emails
1243
+ second-highest-salary
1244
+ group-sold-products-by-the-date
1245
+ list-the-products-ordered-in-a-period
1246
+ find-users-with-valid-e-mails`],
1247
+ ],
1248
+ });
1249
+
1250
+ // Every list, in the order the Progress tab offers them.
1251
+ export const LISTS = [NEETCODE_150, NEETCODE_250, BLIND_75, GRIND_75, TOP_100_LIKED, TOP_INTERVIEW_150, SQL_50];
1252
+ export const ROADMAPS = Object.fromEntries(LISTS.map((list) => [list.id, list]));
1253
+ export const DEFAULT_LIST_ID = NEETCODE_150.id;
1254
+
1255
+ // The list with this id; anything unknown (an old or damaged stored value)
1256
+ // means the default, NeetCode 150.
1257
+ export function listById(id) {
1258
+ return (typeof id === 'string' && Object.hasOwn(ROADMAPS, id) && ROADMAPS[id]) || NEETCODE_150;
1259
+ }
1260
+
1261
+ export function problemUrl(slug) {
1262
+ return `https://leetcode.com/problems/${slug}/`;
1263
+ }
1264
+
1265
+ function index(roadmap) {
1266
+ const bySlug = new Map();
1267
+ for (const topic of roadmap.topics) for (const p of topic.problems) bySlug.set(p.slug, p);
1268
+ return bySlug;
1269
+ }
1270
+
1271
+ // The problem as this list has it, or null when it is not on the list.
1272
+ export function problemInList(roadmap, slug) {
1273
+ return index(roadmap).get(slug) || null;
1274
+ }
1275
+
1276
+ // Solved and total counts per topic and overall. `solved` is any iterable of
1277
+ // slugs (the student's accepted submissions); slugs outside the roadmap are
1278
+ // ignored for the counts.
1279
+ export function progress(roadmap, solved) {
1280
+ const done = new Set(solved);
1281
+ const topics = roadmap.topics.map((topic) => {
1282
+ const solvedCount = topic.problems.filter((p) => done.has(p.slug)).length;
1283
+ return { id: topic.id, title: topic.title, solved: solvedCount, total: topic.problems.length, complete: solvedCount === topic.problems.length };
1284
+ });
1285
+ const solvedTotal = topics.reduce((n, t) => n + t.solved, 0);
1286
+ const total = topics.reduce((n, t) => n + t.total, 0);
1287
+ return { topics, solved: solvedTotal, total };
1288
+ }
1289
+
1290
+ // A topic is unlocked when every topic that points to it has at least one
1291
+ // problem solved (the roadmap is a guide, not a gate: nothing is blocked,
1292
+ // this only orders the suggestions). A list without edges has every group
1293
+ // unlocked, so its order alone decides.
1294
+ export function unlockedTopics(roadmap, solved) {
1295
+ const done = new Set(solved);
1296
+ const started = new Set(roadmap.topics.filter((t) => t.problems.some((p) => done.has(p.slug))).map((t) => t.id));
1297
+ return new Set(
1298
+ roadmap.topics
1299
+ .filter((t) => roadmap.edges.filter(([, to]) => to === t.id).every(([from]) => started.has(from)))
1300
+ .map((t) => t.id)
1301
+ );
1302
+ }
1303
+
1304
+ // What to do next, in the list's order: the first unsolved problems of the
1305
+ // earliest topic that is unlocked and not finished.
1306
+ export function nextOnRoadmap(roadmap, solved, { count = 1 } = {}) {
1307
+ const done = new Set(solved);
1308
+ const open = unlockedTopics(roadmap, done);
1309
+ const out = [];
1310
+ for (const topic of roadmap.topics) {
1311
+ if (!open.has(topic.id)) continue;
1312
+ for (const p of topic.problems) {
1313
+ if (!done.has(p.slug)) out.push(p);
1314
+ if (out.length >= count) return out;
1315
+ }
1316
+ }
1317
+ return out;
1318
+ }
1319
+
1320
+ // More of the same kind: unsolved problems in the topic of the one just
1321
+ // solved (its subject, on a list that has subjects), easiest first.
1322
+ export function moreOfTopic(roadmap, solved, slug, { count = 1 } = {}) {
1323
+ const done = new Set(solved);
1324
+ const problem = index(roadmap).get(slug);
1325
+ if (!problem) return { topic: null, problems: [] };
1326
+ const topic = roadmap.subjects ? roadmap.subjects.find((t) => t.id === problem.subject) : roadmap.topics.find((t) => t.id === problem.topic);
1327
+ const rank = { Easy: 0, Medium: 1, Hard: 2 };
1328
+ const problems = topic.problems
1329
+ .filter((p) => !done.has(p.slug) && p.slug !== slug)
1330
+ .sort((a, b) => rank[a.difficulty] - rank[b.difficulty])
1331
+ .slice(0, count);
1332
+ return { topic: { id: topic.id, title: topic.title }, problems };
1333
+ }
1334
+
1335
+ // Everything the "nice job" message needs after an accepted submission.
1336
+ export function suggestAfterSolve(roadmap, solved, slug, { count = 1 } = {}) {
1337
+ const done = new Set(solved);
1338
+ done.add(slug);
1339
+ return {
1340
+ roadmap: { id: roadmap.id, title: roadmap.title },
1341
+ agenda: nextOnRoadmap(roadmap, done, { count }),
1342
+ sameTopic: moreOfTopic(roadmap, done, slug, { count }),
1343
+ };
1344
+ }