From 38690a9e3576cc602dd31f6cdde6fe63fc66ed7f Mon Sep 17 00:00:00 2001 From: dcq-31 <64748988+dcq-31@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:26:43 +0100 Subject: [PATCH 1/2] feat(concepts): add Prefix Sum Array --- README.md | 4 +- README_ES.md | 4 +- src/content/algorithms/prefix-sum-array.ts | 46 ++++++ src/lib/algorithms/catalog.ts | 7 + src/lib/algorithms/concepts.ts | 172 +++++++++++++++++++++ src/lib/algorithms/cpp/concepts.ts | 17 ++ src/lib/algorithms/index.ts | 2 + src/lib/algorithms/java/concepts.ts | 17 ++ src/lib/algorithms/loaders.ts | 2 + src/lib/algorithms/python/concepts.ts | 13 ++ src/lib/algorithms/rust/concepts.ts | 17 ++ src/lib/types.ts | 18 +++ src/lib/visualizers/concept/index.ts | 3 + src/lib/visualizers/concept/prefix-sum.ts | 155 +++++++++++++++++++ 14 files changed, 473 insertions(+), 4 deletions(-) create mode 100644 src/content/algorithms/prefix-sum-array.ts create mode 100644 src/lib/visualizers/concept/prefix-sum.ts diff --git a/README.md b/README.md index 620647f..2ef9197 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ A free, interactive web tool to learn algorithms through animated step-by-step v - **Variable tracking** — see the state of every variable in real time - **Contextual explanation** — understand the _why_ behind each operation -## 40+ algorithms across 8 categories +## 41+ algorithms across 8 categories @@ -86,7 +86,7 @@ Tower of Hanoi ### Concepts -Big O · Recursion · Two Pointers · Sliding Window · Memoization · Greedy vs DP · Space Complexity +Big O · Recursion · Two Pointers · Sliding Window · Prefix Sum Array · Memoization · Greedy vs DP · Space Complexity diff --git a/README_ES.md b/README_ES.md index c611006..b17d51b 100644 --- a/README_ES.md +++ b/README_ES.md @@ -27,7 +27,7 @@ Una herramienta web interactiva y gratuita para aprender algoritmos a través de - **Seguimiento de variables** — ve el estado de cada variable en tiempo real - **Explicación contextual** — entiende el _porqué_ de cada operación -## +40 algoritmos en 8 categorías +## +41 algoritmos en 8 categorías
@@ -86,7 +86,7 @@ Torre de Hanói ### Conceptos -Big O · Recursión · Two Pointers · Sliding Window · Memoización · Greedy vs DP · Space Complexity +Big O · Recursión · Two Pointers · Sliding Window · Prefix Sum Array · Memoización · Greedy vs DP · Space Complexity diff --git a/src/content/algorithms/prefix-sum-array.ts b/src/content/algorithms/prefix-sum-array.ts new file mode 100644 index 0000000..6995b85 --- /dev/null +++ b/src/content/algorithms/prefix-sum-array.ts @@ -0,0 +1,46 @@ +import type { Locale } from '@i18n/translations' + +const descriptions: Record = { + en: `Prefix Sum Array + +A Prefix Sum Array preprocesses a static array so range-sum queries become O(1). Each position stores the sum of all elements up to that index. + +How it works: +1. Build prefix[0] = arr[0] +2. For each next index, add the current value to the previous prefix +3. Answer sum(l, r) with prefix[r] - prefix[l - 1] +4. If l = 0, the answer is just prefix[r] + +Time Complexity: O(n) preprocessing, O(1) per query +Space Complexity: O(n) + +Best when: + - The array is static + - You need many range-sum queries + - You want to trade one preprocessing pass for instant lookups + +Limitation: + - Point updates are not handled efficiently here; for dynamic updates use other structures such as Fenwick Tree or Segment Tree.`, + es: `Prefix Sum Array + +Un Prefix Sum Array preprocesa un arreglo estático para que las consultas de suma por rango sean O(1). Cada posición guarda la suma de todos los elementos hasta ese índice. + +Cómo funciona: +1. Construir prefix[0] = arr[0] +2. Para cada índice siguiente, sumar el valor actual al prefijo anterior +3. Responder sum(l, r) con prefix[r] - prefix[l - 1] +4. Si l = 0, la respuesta es simplemente prefix[r] + +Complejidad Temporal: O(n) de preprocesamiento, O(1) por consulta +Complejidad Espacial: O(n) + +Conviene cuando: + - El arreglo es estático + - Necesitas muchas consultas de suma por rango + - Quieres cambiar una pasada de preprocesamiento por consultas instantáneas + +Limitación: + - Las actualizaciones puntuales no se manejan eficientemente aquí; para actualizaciones dinámicas hacen falta otras estructuras como Fenwick Tree o Segment Tree.`, +} + +export default descriptions diff --git a/src/lib/algorithms/catalog.ts b/src/lib/algorithms/catalog.ts index 419dc99..e0eba3e 100644 --- a/src/lib/algorithms/catalog.ts +++ b/src/lib/algorithms/catalog.ts @@ -35,6 +35,13 @@ export const algorithmCatalog: AlgorithmSummary[] = [ difficulty: 'intermediate', visualization: 'concept', }, + { + id: 'prefix-sum-array', + name: 'Prefix Sum Array', + category: 'Concepts', + difficulty: 'easy', + visualization: 'concept', + }, { id: 'space-complexity', name: 'Space Complexity', diff --git a/src/lib/algorithms/concepts.ts b/src/lib/algorithms/concepts.ts index f63d20b..e3e1fcd 100644 --- a/src/lib/algorithms/concepts.ts +++ b/src/lib/algorithms/concepts.ts @@ -1205,6 +1205,178 @@ export const slidingWindow: Algorithm = { }, } +// ============================================================ +// PREFIX SUM ARRAY +// ============================================================ + +export const prefixSumArray: Algorithm = { + id: 'prefix-sum-array', + name: 'Prefix Sum Array', + category: 'Concepts', + difficulty: 'easy', + visualization: 'concept', + code: `function buildPrefixSum(arr) { + const prefix = new Array(arr.length); + prefix[0] = arr[0]; + + for (let i = 1; i < arr.length; i++) { + prefix[i] = prefix[i - 1] + arr[i]; + } + return prefix; +} + +function rangeSum(prefix, left, right) { + if (left === 0) { + return prefix[right]; + } + return prefix[right] - prefix[left - 1]; +}`, + + generateSteps(locale = 'en') { + const steps: Step[] = [] + const array = [3, 1, 4, 2, 5] + const fullPrefix = [3, 4, 8, 10, 15] + + const partialPrefix = (filledThrough: number) => + array.map((_, index) => (index <= filledThrough ? fullPrefix[index] : null)) + + steps.push({ + concept: { + type: 'prefixSum', + array, + prefix: array.map(() => null), + phase: 'intro', + currentIndex: null, + range: null, + activePrefixIndices: [], + query: null, + operation: 'preprocess static array', + }, + description: d( + locale, + 'Prefix sums trade one O(n) preprocessing pass for O(1) range-sum queries on a static array.', + 'Los prefix sums cambian una pasada de preprocesamiento O(n) por consultas de suma por rango O(1) sobre un arreglo estático.', + ), + codeLine: 1, + variables: { array: '[3, 1, 4, 2, 5]', preprocessing: 'O(n)', 'per query': 'O(1)' }, + }) + + steps.push({ + concept: { + type: 'prefixSum', + array, + prefix: partialPrefix(0), + phase: 'build', + currentIndex: 0, + range: { start: 0, end: 0 }, + activePrefixIndices: [0], + query: null, + operation: 'build prefix[0]', + }, + description: d( + locale, + 'Initialize the prefix array with the first value. prefix[0] equals arr[0], so the sum from 0 to 0 is already known.', + 'Inicializa el arreglo prefix con el primer valor. prefix[0] es igual a arr[0], así que la suma de 0 a 0 ya queda conocida.', + ), + codeLine: 3, + variables: { i: 0, 'arr[i]': 3, 'prefix[0]': 3 }, + }) + + for (let i = 1; i < array.length; i++) { + steps.push({ + concept: { + type: 'prefixSum', + array, + prefix: partialPrefix(i), + phase: 'build', + currentIndex: i, + range: { start: 0, end: i }, + activePrefixIndices: [i - 1, i], + query: null, + operation: `build prefix[${i}]`, + }, + description: d( + locale, + `Add arr[${i}] = ${array[i]} to the previous prefix. Now prefix[${i}] stores the sum of the whole range [0..${i}] = ${fullPrefix[i]}.`, + `Suma arr[${i}] = ${array[i]} al prefijo anterior. Ahora prefix[${i}] guarda la suma de todo el rango [0..${i}] = ${fullPrefix[i]}.`, + ), + codeLine: i === 1 ? 5 : 6, + variables: { + i, + 'prefix[i - 1]': fullPrefix[i - 1], + 'arr[i]': array[i], + 'prefix[i]': fullPrefix[i], + }, + }) + } + + steps.push({ + concept: { + type: 'prefixSum', + array, + prefix: partialPrefix(array.length - 1), + phase: 'query', + currentIndex: null, + range: { start: 0, end: 2 }, + activePrefixIndices: [2], + query: { left: 0, right: 2, sum: 8, usesBaseCase: true }, + operation: 'query sum(0, 2)', + }, + description: d( + locale, + 'Base case: if the range starts at index 0, the answer is just prefix[right]. sum(0, 2) = prefix[2] = 8.', + 'Caso base: si el rango empieza en el índice 0, la respuesta es simplemente prefix[right]. sum(0, 2) = prefix[2] = 8.', + ), + codeLine: 12, + variables: { left: 0, right: 2, answer: 8, formula: 'prefix[2]' }, + }) + + steps.push({ + concept: { + type: 'prefixSum', + array, + prefix: partialPrefix(array.length - 1), + phase: 'query', + currentIndex: null, + range: { start: 1, end: 4 }, + activePrefixIndices: [0, 4], + query: { left: 1, right: 4, sum: 12, usesBaseCase: false }, + operation: 'query sum(1, 4)', + }, + description: d( + locale, + 'General case: subtract the prefix before the range. sum(1, 4) = prefix[4] - prefix[0] = 15 - 3 = 12.', + 'Caso general: resta el prefijo anterior al rango. sum(1, 4) = prefix[4] - prefix[0] = 15 - 3 = 12.', + ), + codeLine: 15, + variables: { left: 1, right: 4, 'prefix[4]': 15, 'prefix[0]': 3, answer: 12 }, + }) + + steps.push({ + concept: { + type: 'prefixSum', + array, + prefix: partialPrefix(array.length - 1), + phase: 'done', + currentIndex: null, + range: { start: 1, end: 4 }, + activePrefixIndices: [0, 2, 4], + query: { left: 1, right: 4, sum: 12, usesBaseCase: false }, + operation: 'ready for many queries', + }, + description: d( + locale, + 'After one linear build, every later query is O(1). That is why prefix sums shine when the array is static and queries are frequent.', + 'Después de una construcción lineal, cada consulta posterior es O(1). Por eso los prefix sums destacan cuando el arreglo es estático y las consultas son frecuentes.', + ), + codeLine: 11, + variables: { preprocessing: 'O(n)', 'per query': 'O(1)', limitation: 'no dynamic updates' }, + }) + + return steps + }, +} + // ============================================================ // SPACE COMPLEXITY (reuses BigO chart) // ============================================================ diff --git a/src/lib/algorithms/cpp/concepts.ts b/src/lib/algorithms/cpp/concepts.ts index e67c090..65006c6 100644 --- a/src/lib/algorithms/cpp/concepts.ts +++ b/src/lib/algorithms/cpp/concepts.ts @@ -106,6 +106,23 @@ vector mergeSort(vector arr) { return s.substr(bestStart, best); }`), + 'prefix-sum-array': annotated(`vector buildPrefixSum(const vector& arr) { //@1 + vector prefix(arr.size()); + prefix[0] = arr[0]; //@3 + + for (int i = 1; i < (int)arr.size(); i++) { //@5 + prefix[i] = prefix[i - 1] + arr[i]; //@6 + } + return prefix; //@8 +} + +int rangeSum(const vector& prefix, int left, int right) { //@11 + if (left == 0) { //@12 + return prefix[right]; //@13 + } + return prefix[right] - prefix[left - 1]; //@15 +}`), + 'space-complexity': annotated(`// O(1) space — fixed variables //@1 void swap(vector& arr, int i, int j) { //@2 int temp = arr[i]; diff --git a/src/lib/algorithms/index.ts b/src/lib/algorithms/index.ts index c646800..064b5d5 100644 --- a/src/lib/algorithms/index.ts +++ b/src/lib/algorithms/index.ts @@ -5,6 +5,7 @@ import { recursion, twoPointers, slidingWindow, + prefixSumArray, spaceComplexity, memoization, greedyVsDp, @@ -71,6 +72,7 @@ export const algorithms: Algorithm[] = [ recursion, twoPointers, slidingWindow, + prefixSumArray, spaceComplexity, memoization, greedyVsDp, diff --git a/src/lib/algorithms/java/concepts.ts b/src/lib/algorithms/java/concepts.ts index 49b5ecd..2e55a16 100644 --- a/src/lib/algorithms/java/concepts.ts +++ b/src/lib/algorithms/java/concepts.ts @@ -103,6 +103,23 @@ int[] mergeSort(int[] arr) { return s.substring(bestStart, bestStart + best); }`), + 'prefix-sum-array': annotated(`int[] buildPrefixSum(int[] arr) { //@1 + int[] prefix = new int[arr.length]; + prefix[0] = arr[0]; //@3 + + for (int i = 1; i < arr.length; i++) { //@5 + prefix[i] = prefix[i - 1] + arr[i]; //@6 + } + return prefix; //@8 +} + +int rangeSum(int[] prefix, int left, int right) { //@11 + if (left == 0) { //@12 + return prefix[right]; //@13 + } + return prefix[right] - prefix[left - 1]; //@15 +}`), + 'space-complexity': annotated(`// O(1) space — fixed variables //@1 void swap(int[] arr, int i, int j) { //@2 int temp = arr[i]; diff --git a/src/lib/algorithms/loaders.ts b/src/lib/algorithms/loaders.ts index 71f2027..563290a 100644 --- a/src/lib/algorithms/loaders.ts +++ b/src/lib/algorithms/loaders.ts @@ -38,6 +38,8 @@ const ALGORITHM_LOADERS: Record Promise> = { recursion: () => import('./concepts?algorithm=recursion').then(readDefaultAlgorithm), 'two-pointers': () => import('./concepts?algorithm=twoPointers').then(readDefaultAlgorithm), 'sliding-window': () => import('./concepts?algorithm=slidingWindow').then(readDefaultAlgorithm), + 'prefix-sum-array': () => + import('./concepts?algorithm=prefixSumArray').then(readDefaultAlgorithm), 'space-complexity': () => import('./concepts?algorithm=spaceComplexity').then(readDefaultAlgorithm), memoization: () => import('./concepts?algorithm=memoization').then(readDefaultAlgorithm), diff --git a/src/lib/algorithms/python/concepts.ts b/src/lib/algorithms/python/concepts.ts index 851c696..93cd5f5 100644 --- a/src/lib/algorithms/python/concepts.ts +++ b/src/lib/algorithms/python/concepts.ts @@ -96,6 +96,19 @@ def merge(left, right): best_start = start return s[best_start:best_start + best]`), + 'prefix-sum-array': annotated(`def build_prefix_sum(arr): #@1 + prefix = [0] * len(arr) + prefix[0] = arr[0] #@3 + + for i in range(1, len(arr)): #@5 + prefix[i] = prefix[i - 1] + arr[i] #@6 + return prefix #@8 + +def range_sum(prefix, left, right): #@11 + if left == 0: #@12 + return prefix[right] #@13 + return prefix[right] - prefix[left - 1] #@15`), + 'space-complexity': annotated(`# O(1) space — fixed variables #@1 def swap(arr, i, j): #@2 temp = arr[i] diff --git a/src/lib/algorithms/rust/concepts.ts b/src/lib/algorithms/rust/concepts.ts index 2b25915..50a1e3c 100644 --- a/src/lib/algorithms/rust/concepts.ts +++ b/src/lib/algorithms/rust/concepts.ts @@ -112,6 +112,23 @@ fn merge_sort(arr: &[i32]) -> Vec { chars[best_start..best_start + best].iter().collect() }`), + 'prefix-sum-array': annotated(`fn build_prefix_sum(arr: &[i32]) -> Vec { //@1 + let mut prefix = vec![0; arr.len()]; + prefix[0] = arr[0]; //@3 + + for i in 1..arr.len() { //@5 + prefix[i] = prefix[i - 1] + arr[i]; //@6 + } + prefix //@8 +} + +fn range_sum(prefix: &[i32], left: usize, right: usize) -> i32 { //@11 + if left == 0 { //@12 + return prefix[right]; //@13 + } + prefix[right] - prefix[left - 1] //@15 +}`), + 'space-complexity': annotated(`// O(1) space — fixed variables //@1 fn swap(arr: &mut [i32], i: usize, j: usize) { //@2 let temp = arr[i]; diff --git a/src/lib/types.ts b/src/lib/types.ts index b5ef125..55ee6fa 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -206,6 +206,23 @@ export interface SlidingWindowState { operation?: string } +export interface PrefixSumState { + type: 'prefixSum' + array: number[] + prefix: (number | null)[] + phase: 'intro' | 'build' | 'query' | 'done' + currentIndex?: number | null + range?: { start: number; end: number } | null + activePrefixIndices?: number[] + query?: { + left: number + right: number + sum: number + usesBaseCase: boolean + } | null + operation?: string +} + export interface MemoTableState { type: 'memoTable' entries: { key: number; value: number | null; state: 'empty' | 'computing' | 'cached' | 'hit' }[] @@ -424,6 +441,7 @@ export type ConceptState = | LruCacheState | TwoPointersState | SlidingWindowState + | PrefixSumState | MemoTableState | CoinChangeState | BucketsState diff --git a/src/lib/visualizers/concept/index.ts b/src/lib/visualizers/concept/index.ts index 2e68fe9..621a893 100644 --- a/src/lib/visualizers/concept/index.ts +++ b/src/lib/visualizers/concept/index.ts @@ -14,6 +14,7 @@ export type ConceptType = | 'lruCache' | 'twoPointers' | 'slidingWindow' + | 'prefixSum' | 'memoTable' | 'coinChange' | 'buckets' @@ -56,6 +57,8 @@ const loaders: Record Promise> = { import('@lib/visualizers/concept/sliding-window').then( (m) => m.renderSlidingWindow as ConceptRenderer, ), + prefixSum: () => + import('@lib/visualizers/concept/prefix-sum').then((m) => m.renderPrefixSum as ConceptRenderer), memoTable: () => import('@lib/visualizers/concept/memo-table').then((m) => m.renderMemoTable as ConceptRenderer), coinChange: () => diff --git a/src/lib/visualizers/concept/prefix-sum.ts b/src/lib/visualizers/concept/prefix-sum.ts new file mode 100644 index 0000000..70a2e66 --- /dev/null +++ b/src/lib/visualizers/concept/prefix-sum.ts @@ -0,0 +1,155 @@ +/** + * Concept visualizer: PrefixSum. + */ +import type { PrefixSumState } from '@lib/types' +import { applyStyles } from '@lib/visualizers/concept/dom' + +const ARRAY_COLORS = { + default: { bg: 'var(--subtle)', border: 'var(--viz-border)', text: 'var(--viz-label)' }, + current: { bg: 'rgba(96,165,250,0.15)', border: 'rgba(96,165,250,0.38)', text: '#60a5fa' }, + inRange: { bg: 'rgba(74,222,128,0.16)', border: 'rgba(74,222,128,0.4)', text: '#86efac' }, +} + +const PREFIX_COLORS = { + empty: { bg: 'rgba(255,255,255,0.03)', border: 'var(--viz-border)', text: 'var(--viz-muted)' }, + ready: { bg: 'rgba(250,204,21,0.12)', border: 'rgba(250,204,21,0.32)', text: '#fde047' }, + active: { bg: 'rgba(251,146,60,0.14)', border: 'rgba(251,146,60,0.4)', text: '#fb923c' }, +} + +function makeCell( + value: string, + colors: { bg: string; border: string; text: string }, + wide = false, +) { + const cell = document.createElement('div') + cell.className = `${wide ? 'w-16 md:w-18' : 'w-14 md:w-16'} h-12 rounded-lg border flex items-center justify-center font-mono text-sm md:text-base font-bold transition-all duration-300` + applyStyles(cell, { + backgroundColor: colors.bg, + borderColor: colors.border, + color: colors.text, + boxShadow: colors.border !== 'var(--viz-border)' ? `0 0 12px ${colors.border}` : 'none', + }) + cell.textContent = value + return cell +} + +function makeIndexRow(length: number, wide = false) { + const row = document.createElement('div') + row.className = 'flex gap-1' + for (let i = 0; i < length; i++) { + const idx = document.createElement('div') + idx.className = `${wide ? 'w-16 md:w-18' : 'w-14 md:w-16'} text-center text-[9px] font-mono text-neutral-600` + idx.textContent = String(i) + row.append(idx) + } + return row +} + +export function renderPrefixSum(state: PrefixSumState): HTMLElement { + const { + array, + prefix, + phase, + currentIndex, + range, + activePrefixIndices = [], + query, + operation, + } = state + + const wrap = document.createElement('div') + wrap.className = 'flex-1 flex flex-col items-center justify-center gap-4 w-full' + + const title = document.createElement('div') + title.className = 'text-neutral-500 font-mono text-[11px] uppercase tracking-widest' + title.textContent = 'Prefix Sum Array' + wrap.append(title) + + if (operation) { + const badge = document.createElement('div') + badge.className = + 'font-mono text-xs px-3 py-1 rounded-full bg-white/5 border border-white/10 text-neutral-300' + badge.textContent = operation + wrap.append(badge) + } + + const table = document.createElement('div') + table.className = 'flex flex-col gap-3 items-center' + + const arraySection = document.createElement('div') + arraySection.className = 'flex flex-col items-center gap-1' + const arrayLabel = document.createElement('div') + arrayLabel.className = 'text-[10px] font-mono uppercase tracking-[0.22em] text-neutral-500' + arrayLabel.textContent = 'Original Array' + arraySection.append(arrayLabel) + + const arrayCells = document.createElement('div') + arrayCells.className = 'flex gap-1' + array.forEach((value, index) => { + const inRange = range && index >= range.start && index <= range.end + const colors = + index === currentIndex + ? ARRAY_COLORS.current + : inRange + ? ARRAY_COLORS.inRange + : ARRAY_COLORS.default + arrayCells.append(makeCell(String(value), colors)) + }) + arraySection.append(arrayCells, makeIndexRow(array.length)) + table.append(arraySection) + + const prefixSection = document.createElement('div') + prefixSection.className = 'flex flex-col items-center gap-1' + const prefixLabel = document.createElement('div') + prefixLabel.className = 'text-[10px] font-mono uppercase tracking-[0.22em] text-neutral-500' + prefixLabel.textContent = 'Prefix Array' + prefixSection.append(prefixLabel) + + const prefixCells = document.createElement('div') + prefixCells.className = 'flex gap-1' + prefix.forEach((value, index) => { + const isActive = activePrefixIndices.includes(index) + const colors = + value == null ? PREFIX_COLORS.empty : isActive ? PREFIX_COLORS.active : PREFIX_COLORS.ready + prefixCells.append(makeCell(value == null ? '·' : String(value), colors, true)) + }) + prefixSection.append(prefixCells, makeIndexRow(prefix.length, true)) + table.append(prefixSection) + + wrap.append(table) + + if ( + phase === 'build' && + currentIndex != null && + currentIndex > 0 && + prefix[currentIndex] != null + ) { + const formula = document.createElement('div') + formula.className = 'font-mono text-xs text-neutral-400 text-center' + formula.innerHTML = `prefix[${currentIndex}] = prefix[${currentIndex - 1}] + arr[${currentIndex}] = ${prefix[currentIndex - 1]} + ${array[currentIndex]} = ${prefix[currentIndex]}` + wrap.append(formula) + } + + if (query && range) { + const line = document.createElement('div') + line.className = 'font-mono text-xs md:text-sm text-neutral-300 text-center' + if (query.usesBaseCase) { + line.innerHTML = `sum(${query.left}, ${query.right}) = prefix[${query.right}] = ${query.sum}` + } else { + const leftPrefix = prefix[query.left - 1] + const rightPrefix = prefix[query.right] + line.innerHTML = `sum(${query.left}, ${query.right}) = prefix[${query.right}] - prefix[${query.left - 1}] = ${rightPrefix} - ${leftPrefix} = ${query.sum}` + } + wrap.append(line) + } + + if (phase === 'done') { + const summary = document.createElement('div') + summary.className = 'font-mono text-xs text-neutral-400 text-center max-w-xl' + summary.textContent = + 'One O(n) preprocessing pass turns repeated range sums into O(1) lookups on a static array.' + wrap.append(summary) + } + + return wrap +} From d50076b4952e43a7ae05b2ba04b300cda0f5df69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel=20Dur=C3=A1n=20=28midudev=29?= Date: Mon, 28 Sep 2026 16:59:36 +0200 Subject: [PATCH 2/2] fix(concepts): polish Prefix Sum Array visualization - Highlight the line that computes prefix[i] on every build step and the return line on the base-case query - Drop the redundant English-only summary on the last step (it overflowed the stage on mobile) - Use higher-contrast text colors so values stay readable in light theme - Add Open Graph images for prefix-sum-array (and the missing euclidean) - Keep README headline at 40+ algorithms --- README.md | 2 +- README_ES.md | 2 +- public/og/euclidean.jpg | Bin 0 -> 34172 bytes public/og/prefix-sum-array.jpg | Bin 0 -> 36347 bytes scripts/generate-og.mjs | 1 + src/lib/algorithms/concepts.ts | 4 ++-- src/lib/visualizers/concept/prefix-sum.ts | 14 +++----------- 7 files changed, 8 insertions(+), 15 deletions(-) create mode 100644 public/og/euclidean.jpg create mode 100644 public/og/prefix-sum-array.jpg diff --git a/README.md b/README.md index 2ef9197..c0a7300 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ A free, interactive web tool to learn algorithms through animated step-by-step v - **Variable tracking** — see the state of every variable in real time - **Contextual explanation** — understand the _why_ behind each operation -## 41+ algorithms across 8 categories +## 40+ algorithms across 8 categories
diff --git a/README_ES.md b/README_ES.md index b17d51b..b86d845 100644 --- a/README_ES.md +++ b/README_ES.md @@ -27,7 +27,7 @@ Una herramienta web interactiva y gratuita para aprender algoritmos a través de - **Seguimiento de variables** — ve el estado de cada variable en tiempo real - **Explicación contextual** — entiende el _porqué_ de cada operación -## +41 algoritmos en 8 categorías +## +40 algoritmos en 8 categorías
diff --git a/public/og/euclidean.jpg b/public/og/euclidean.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fa74322b8f606c66480d76a3b7053e7906e4e88b GIT binary patch literal 34172 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

SJ&NgDX)7YH?X`1+! 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 literal 0 HcmV?d00001 diff --git a/public/og/prefix-sum-array.jpg b/public/og/prefix-sum-array.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d94cf1b0bf0aaa5cc31ace763e319a3cadfd3e4f GIT binary patch literal 36347 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