Skip to content

Commit a39ed89

Browse files
committed
feat: add group anagrams visualization utilizing hash map with frequency array
Signed-off-by: Ayush Joshi <ayush854032@gmail.com>
1 parent a917085 commit a39ed89

5 files changed

Lines changed: 936 additions & 0 deletions

File tree

algos/strings/group_anagrams.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Given an array of strings strs, group the anagrams together. You can return the
2+
answer in any order.
3+
"""
4+
5+
from collections import defaultdict
6+
from typing import List
7+
8+
9+
class Solution:
10+
def groupAnagrams(self, strs: List[str]) -> List[List[str]]:
11+
group = defaultdict(list)
12+
for word in strs:
13+
hash = [0] * 26
14+
for ch in word:
15+
hash[ord(ch) - ord("a")] += 1
16+
group[tuple(hash)].append(word)
17+
return list(group.values())

visualizations/index.html

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,34 @@ <h3>Longest Palindromic Substring</h3>
409409
</svg>
410410
</div>
411411
</a>
412+
<a href="strings/group-anagrams/index.html" class="algo-card">
413+
<div class="algo-content">
414+
<h3>Group Anagrams</h3>
415+
<p>Group words with the same character frequencies using a hashmap.</p>
416+
<span class="algo-tag">HashMap</span>
417+
</div>
418+
<div class="algo-svg">
419+
<svg viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
420+
<!-- Words scattered on top -->
421+
<rect x="5" y="8" width="22" height="12" rx="3" fill="#4ecdc4"/>
422+
<text x="16" y="17" fill="#1a1a2e" font-size="7" text-anchor="middle" font-family="monospace">eat</text>
423+
<rect x="30" y="8" width="22" height="12" rx="3" fill="#ff6b6b"/>
424+
<text x="41" y="17" fill="#fff" font-size="7" text-anchor="middle" font-family="monospace">tan</text>
425+
<rect x="55" y="8" width="22" height="12" rx="3" fill="#4ecdc4" class="animate-on-hover"/>
426+
<text x="66" y="17" fill="#1a1a2e" font-size="7" text-anchor="middle" font-family="monospace">tea</text>
427+
<!-- Arrow down -->
428+
<path d="M40 24 L40 32" stroke="#00d9ff" stroke-width="1.5"/>
429+
<path d="M37 29 L40 34 L43 29" stroke="#00d9ff" stroke-width="1.5" fill="none"/>
430+
<!-- HashMap buckets -->
431+
<rect x="5" y="40" width="32" height="28" rx="4" fill="none" stroke="#4ecdc4" stroke-width="1.5"/>
432+
<text x="21" y="52" fill="#4ecdc4" font-size="6" text-anchor="middle" font-family="monospace">eat</text>
433+
<text x="21" y="62" fill="#4ecdc4" font-size="6" text-anchor="middle" font-family="monospace">tea, ate</text>
434+
<rect x="43" y="40" width="32" height="28" rx="4" fill="none" stroke="#ff6b6b" stroke-width="1.5"/>
435+
<text x="59" y="52" fill="#ff6b6b" font-size="6" text-anchor="middle" font-family="monospace">tan</text>
436+
<text x="59" y="62" fill="#ff6b6b" font-size="6" text-anchor="middle" font-family="monospace">nat</text>
437+
</svg>
438+
</div>
439+
</a>
412440
</div>
413441
</div>
414442

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<!-- Google Analytics -->
5+
<script async src="https://www.googletagmanager.com/gtag/js?id=G-CKLM4BWJFH"></script>
6+
<script>
7+
window.dataLayer = window.dataLayer || [];
8+
function gtag(){dataLayer.push(arguments);}
9+
gtag('js', new Date());
10+
gtag('config', 'G-CKLM4BWJFH');
11+
</script>
12+
<meta charset="UTF-8">
13+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
14+
<title>Group Anagrams</title>
15+
<link rel="stylesheet" href="../../shared/css/main.css">
16+
<link rel="stylesheet" href="style.css">
17+
</head>
18+
<body>
19+
<div class="container">
20+
<a href="../../" class="back-link">&larr; Back to all algorithms</a>
21+
<h1>Group Anagrams</h1>
22+
<p class="description">Group words by character frequency using a hashmap</p>
23+
24+
<div class="controls">
25+
<div class="input-group">
26+
<label for="wordsInput">Words:</label>
27+
<input type="text" id="wordsInput" value="eat, tea, tan, ate, nat, bat" placeholder="e.g., eat, tea, tan">
28+
</div>
29+
<button class="btn-primary" id="resetBtn">Reset</button>
30+
<button class="btn-secondary" id="stepBtn">Step</button>
31+
<button class="btn-success" id="playBtn">Play</button>
32+
<div class="speed-control">
33+
<label for="speedSlider">Speed:</label>
34+
<input type="range" id="speedSlider" min="100" max="2000" value="800">
35+
</div>
36+
</div>
37+
38+
<div class="main-content">
39+
<div class="visualization">
40+
<!-- Word List -->
41+
<div class="section-title">Words</div>
42+
<div class="word-list" id="wordListDisplay"></div>
43+
44+
<!-- Current Word Processing -->
45+
<div class="processing-section">
46+
<div class="section-title">Current Word</div>
47+
<div class="current-word-display" id="currentWordDisplay">-</div>
48+
</div>
49+
50+
<!-- Frequency Array -->
51+
<div class="freq-section">
52+
<div class="section-title">Frequency Array (a-z)</div>
53+
<div class="freq-grid" id="freqDisplay"></div>
54+
<div class="freq-labels" id="freqLabels"></div>
55+
</div>
56+
57+
<!-- Hash Key -->
58+
<div class="hashkey-section">
59+
<div class="section-title">Hash Key (tuple)</div>
60+
<div class="hashkey-display" id="hashKeyDisplay">-</div>
61+
</div>
62+
63+
<!-- HashMap -->
64+
<div class="hashmap-container">
65+
<div class="section-title">HashMap (frequency &rarr; words)</div>
66+
<div class="hashmap" id="hashmapDisplay">
67+
<span class="empty-text">Empty</span>
68+
</div>
69+
</div>
70+
71+
<!-- Info Panel -->
72+
<div class="info-panel">
73+
<div class="info-box">
74+
<h3>Word Index</h3>
75+
<div class="info-value" id="wordIndexValue">-</div>
76+
</div>
77+
<div class="info-box">
78+
<h3>Current Word</h3>
79+
<div class="info-value" id="currentWordValue">-</div>
80+
</div>
81+
<div class="info-box">
82+
<h3>Char Scanning</h3>
83+
<div class="info-value" id="charScanValue">-</div>
84+
</div>
85+
<div class="info-box">
86+
<h3>Groups Found</h3>
87+
<div class="info-value" id="groupCountValue">0</div>
88+
</div>
89+
</div>
90+
91+
<!-- Status -->
92+
<div class="status" id="status">
93+
Click "Step" or "Play" to start the visualization
94+
</div>
95+
96+
<!-- Legend -->
97+
<div class="legend">
98+
<div class="legend-item">
99+
<div class="legend-color current"></div>
100+
<span>Current Word</span>
101+
</div>
102+
<div class="legend-item">
103+
<div class="legend-color highlight"></div>
104+
<span>Processed</span>
105+
</div>
106+
<div class="legend-item">
107+
<div class="legend-color found"></div>
108+
<span>Grouped</span>
109+
</div>
110+
</div>
111+
</div>
112+
113+
<!-- Code Panel -->
114+
<div class="code-panel">
115+
<h3>Python Code</h3>
116+
<div class="code-line" id="code-0">
117+
<span class="code-variable">group</span> = <span class="code-function">defaultdict</span>(<span class="code-function">list</span>)
118+
</div>
119+
<div class="code-line" id="code-1">
120+
<span class="code-keyword">for</span> <span class="code-variable">word</span> <span class="code-keyword">in</span> strs:
121+
</div>
122+
<div class="code-line" id="code-2">
123+
&nbsp;&nbsp;&nbsp;&nbsp;<span class="code-variable">hash</span> = [<span class="code-number">0</span>] * <span class="code-number">26</span>
124+
</div>
125+
<div class="code-line" id="code-3">
126+
&nbsp;&nbsp;&nbsp;&nbsp;<span class="code-keyword">for</span> <span class="code-variable">ch</span> <span class="code-keyword">in</span> word:
127+
</div>
128+
<div class="code-line" id="code-4">
129+
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hash[<span class="code-function">ord</span>(ch) - <span class="code-function">ord</span>(<span class="code-string">"a"</span>)] += <span class="code-number">1</span>
130+
</div>
131+
<div class="code-line" id="code-5">
132+
&nbsp;&nbsp;&nbsp;&nbsp;<span class="code-variable">group</span>[<span class="code-function">tuple</span>(hash)].append(word)
133+
</div>
134+
<div class="code-line" id="code-6">
135+
<span class="code-keyword">return</span> <span class="code-function">list</span>(group.values())
136+
</div>
137+
</div>
138+
</div>
139+
140+
<div class="complexity-section">
141+
<input type="checkbox" id="complexity-toggle" class="complexity-toggle-input" checked>
142+
<label for="complexity-toggle" class="complexity-header">
143+
<span class="complexity-title">Complexity Analysis</span>
144+
<span class="complexity-icon"></span>
145+
</label>
146+
<div class="complexity-content">
147+
<input type="radio" id="tab-time" name="complexity-tab" class="complexity-tab-input" checked>
148+
<input type="radio" id="tab-space" name="complexity-tab" class="complexity-tab-input">
149+
<div class="complexity-columns">
150+
<div class="complexity-stats">
151+
<label for="tab-time" class="complexity-item">
152+
<span class="complexity-label">Time Complexity</span>
153+
<span class="complexity-value">O(n &middot; k)</span>
154+
</label>
155+
<label for="tab-space" class="complexity-item">
156+
<span class="complexity-label">Space Complexity</span>
157+
<span class="complexity-value">O(n &middot; k)</span>
158+
</label>
159+
</div>
160+
<div class="complexity-explanation">
161+
<div class="complexity-detail time-detail">
162+
<h4 class="detail-title">Time Complexity: <span class="big-o">O(n &middot; k)</span></h4>
163+
<div class="detail-section">
164+
<div class="detail-section-title">Breakdown</div>
165+
<div class="detail-content">
166+
<ul class="detail-list">
167+
<li>Iterate through n words in the input list</li>
168+
<li>For each word, scan k characters to build frequency array: O(k)</li>
169+
<li>Tuple creation from 26-element array: O(26) = O(1)</li>
170+
<li>HashMap lookup and insertion: O(1) amortized</li>
171+
</ul>
172+
<div class="detail-formula">T(n, k) = n &times; (k + 26 + 1) = O(n &middot; k)</div>
173+
</div>
174+
</div>
175+
<div class="detail-section">
176+
<div class="detail-section-title">Mathematical Intuition</div>
177+
<div class="detail-content">
178+
<p>The sorting-based approach sorts each word's characters in O(k log k), yielding O(n &middot; k log k) total. By counting character frequencies instead of sorting, we reduce per-word cost from O(k log k) to O(k), exploiting the fixed 26-letter alphabet as a constant-size domain.</p>
179+
</div>
180+
</div>
181+
<div class="detail-insight">
182+
<span class="detail-insight-label">Key Insight</span>
183+
<p>The 26-element frequency array acts as a perfect hash for anagram equivalence &mdash; two words are anagrams if and only if they have identical character frequency distributions.</p>
184+
</div>
185+
</div>
186+
<div class="complexity-detail space-detail">
187+
<h4 class="detail-title">Space Complexity: <span class="big-o">O(n &middot; k)</span></h4>
188+
<div class="detail-section">
189+
<div class="detail-section-title">Breakdown</div>
190+
<div class="detail-content">
191+
<ul class="detail-list">
192+
<li>HashMap keys: at most n tuples, each fixed size 26 = O(n)</li>
193+
<li>HashMap values: all n words stored, total chars = O(n &middot; k)</li>
194+
<li>Frequency array: O(26) = O(1), reused per word</li>
195+
<li>Output: all words grouped = O(n &middot; k)</li>
196+
</ul>
197+
<div class="detail-formula">S(n, k) = O(n &middot; k) for storing all words in groups</div>
198+
</div>
199+
</div>
200+
<div class="detail-section">
201+
<div class="detail-section-title">Mathematical Intuition</div>
202+
<div class="detail-content">
203+
<p>Every input word must appear in exactly one output group. Since we store all n words in the hashmap, and the total character count across all words is n &middot; k, the space is necessarily O(n &middot; k). The 26-element frequency array adds only O(1) overhead per iteration.</p>
204+
</div>
205+
</div>
206+
<div class="detail-insight">
207+
<span class="detail-insight-label">Key Insight</span>
208+
<p>The space is dominated by the output itself &mdash; any algorithm that groups n words of average length k must use at least O(n &middot; k) space to store the result.</p>
209+
</div>
210+
</div>
211+
</div>
212+
</div>
213+
</div>
214+
</div>
215+
</div>
216+
217+
<script src="../../shared/js/visualizer.js"></script>
218+
<script src="script.js"></script>
219+
</body>
220+
</html>

0 commit comments

Comments
 (0)