From ec81f7efb30ac9e24a027d8628d136ecf63ab937 Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Thu, 13 Aug 2026 19:38:08 -0400 Subject: [PATCH] feat(work): add Go solution stub for first unique character and remove outdated JS files --- work/Blind/deduction.answers.js | 151 ------------- work/Blind/deduction.js | 83 ------- work/Blind/patterns.js | 211 ------------------ .../387.first-unique-character-in-a-string.go | 47 ++++ 4 files changed, 47 insertions(+), 445 deletions(-) delete mode 100644 work/Blind/deduction.answers.js delete mode 100644 work/Blind/deduction.js delete mode 100644 work/Blind/patterns.js create mode 100644 work/Easy/Hash Table/387.first-unique-character-in-a-string.go diff --git a/work/Blind/deduction.answers.js b/work/Blind/deduction.answers.js deleted file mode 100644 index 4d54034..0000000 --- a/work/Blind/deduction.answers.js +++ /dev/null @@ -1,151 +0,0 @@ -// ============================================================ -// ANSWER KEY — open only after narrating all 6 out loud. -// Solutions match your repo style exactly: -// ~/Developer/github.com/prdlk/leetcode/work/Default -// (object freq maps, (freq[n] || 0) + 1, same shapes) -// ============================================================ - -// problem1 = LC 1365 — How Many Numbers Are Smaller Than Current -// (your file: Easy/Array/1365...js — this IS your solution) -// RULE: "output[i] = count of elements strictly smaller than arr[i]; -// ties don't count as smaller (that's the [7,7,7,7] example)." -function problem1(nums) { - // Step 1: Begin by initializing a [Frequency Map] - const freq = {}; - for (let n of nums) freq[n] = (freq[n] || 0) + 1; - - // Step 2: Sort the numbers by ascending order - const sorted = Object.keys(freq).sort((a, b) => a - b); - - // Step 3: Init a count of numbers smaller than the active number - let count = 0; - - // Step 4: Init a map to track number of values smaller for each number - const smaller = {}; - - // Step 5: Iterate over the sorted list - for (let num of sorted) { - // Set count for active number — BEFORE adding own frequency, - // so duplicates only see values strictly below them - smaller[num] = count; - - // Update the count by frequency - count += freq[num]; - } - - // Step 6: Use original list and find number of smaller values than it - return nums.map((n) => smaller[n]); -} -// Narration reminders: -// - "record before adding" is WHY [7,7,7,7] => [0,0,0,0] -// - Object.keys returns strings; (a, b) => a - b coerces numerically -// - quick brute-force alternative if short on time: -// nums.map((n) => nums.filter((m) => m < n).length) - -// problem2 = LC 451 — Sort Characters By Frequency (+ alpha tiebreak) -// (your file: Medium/Hash Table/451...js — same, tiebreak included) -// RULE: "rebuild the string most-frequent chars first; equal counts -// break alphabetically." -// DISCRIMINATOR: "bookkeeper" — e:3, then k:2/o:2 tie -> k before o -// = alphabetical, NOT input order (o appeared first in the input!). -function problem2(s) { - // count the frequency of each character - const freq = {}; - for (let c of s) freq[c] = (freq[c] || 0) + 1; - - // sort the characters by frequency, ties alphabetical - return s - .split("") - .sort((a, b) => freq[b] - freq[a] || a.localeCompare(b)) - .join(""); -} -// Note vs your repo file: identical. localeCompare orders lowercase -// before uppercase ("bbaA"), which is what the drill examples use. -// LC 451 proper accepts any tie order — the tiebreak is the -// interview twist Jim's source described. - -// problem3 = LC 1636 — Sort Array by Increasing Frequency -// (your file: Easy/Array/1636...js — this IS your solution) -// RULE: "sort by frequency ascending; ties by VALUE DESCENDING." -// DISCRIMINATOR: [2,3,1,3,2] -> 2 and 3 both appear twice, 3 first. -function problem3(nums) { - const freq = {}; - for (let n of nums) freq[n] = (freq[n] || 0) + 1; - return nums.sort((a, b) => freq[a] - freq[b] || b - a); -} -// The idiom to say out loud: "primary key OR tiebreak — when the -// frequency difference is 0 (falsy), JS falls through to b - a." - -// problem4 = LC 387 — First Unique Character -// (your file: Easy/Hash Table/387...js — this IS your solution) -// RULE: "index of the first character appearing exactly once; -1 if none." -// SHAPE TELL: output is a NUMBER, not an array. -function problem4(s) { - const freq = {}; - for (let c of s) freq[c] = (freq[c] || 0) + 1; - - for (let i = 0; i < s.length; i++) { - if (freq[s[i]] === 1) { - return i; - } - } - return -1; -} -// Say it: "two passes — I can't know a char is unique until I've -// seen the whole string." - -// problem5 = LC 242 — Valid Anagram -// (not in your repo yet — written in your exact style) -// RULE: "true iff both strings have the same characters with the -// same counts." -// DISCRIMINATOR: ("aacc", "ccac") -> same char SET {a,c}, different -// counts -> false. Kills set-equality. -function problem5(s, t) { - if (s.length !== t.length) return false; - - const freq = {}; - for (let c of s) freq[c] = (freq[c] || 0) + 1; - - // walk t, spending counts down; a missing/exhausted char fails - for (let c of t) { - if (!freq[c]) return false; - freq[c]--; - } - return true; -} -// The length guard up front is what lets count-down work without a -// final "all zeros" pass. - -// problem6 = LC 347 — Top K Frequent Elements -// (your file: Medium/Array/347...js — this IS your solution) -// RULE: "return the k values that appear most often, most frequent -// first." -// SHAPE TELL: second argument k controls output length. -// EDGE: [3,0,1,0] k=1 => [0] — value 0 is falsy but valid. -function problem6(nums, k) { - const freq = {}; - for (let n of nums) freq[n] = (freq[n] || 0) + 1; - - return Object.keys(freq) - .map(Number) // Object.keys gave us strings — convert back - .sort((a, b) => freq[b] - freq[a]) - .slice(0, k); -} -// The .map(Number) is the classic gotcha to mention: without it you -// return ["1","2"] instead of [1,2]. - -// ============================================================ -// THE HAMMER (all six are this skeleton): -// 1. BUILD -> const freq = {}; for (let x of input) -// freq[x] = (freq[x] || 0) + 1; -// 2. ORDER -> sort keys/elements by the criterion the pattern demands -// 3. DERIVE -> compute what each key maps to (count / rank / index) -// 4. EMIT -> map back to input order / rebuild string / slice k -// -// SELF-SCORE per problem: -// Rule stated in one sentence, verified vs ALL examples: /1 -// Tiebreak/edge named BEFORE coding (the discriminator): /1 -// Working code, narrated while typing: /1 -// 15+/18 = ready. Misses tell you what to re-drill Tuesday -// morning (max 2 reps, then stop — rest beats an 11th rep). -// ============================================================ diff --git a/work/Blind/deduction.js b/work/Blind/deduction.js deleted file mode 100644 index 9f11ac4..0000000 --- a/work/Blind/deduction.js +++ /dev/null @@ -1,83 +0,0 @@ - -// ============================================================ -// BLIND DEDUCTION SET — Monday evening, ONE round, then stop. -// The 6 core interview problems, disguised exactly as they'd -// appear Tuesday: no statement, just input => output pairs. -// Rules: deduce the rule, SAY it in one sentence out loud, -// state the plan (freq map? sort? tiebreak?), then implement. -// Do NOT open blind-deduction-answers.js until finished. -// Target: rule stated < 3 min, implemented < 6 min each. -// ============================================================ - -// ---- problem1 ---- -// problem1([8, 1, 2, 2, 3]) => [4, 0, 1, 1, 3] -// problem1([6, 5, 4, 8]) => [2, 1, 0, 3] -// problem1([7, 7, 7, 7]) => [0, 0, 0, 0] -// problem1([3, 1, 2]) => [2, 0, 1] -// problem1([5]) => [0] -// problem1([4, 1, 4, 1]) => [2, 0, 2, 0] -function problem1(arr) { - // your code -} - -// ---- problem2 ---- -// problem2("tree") => "eert" -// problem2("cccaaa") => "aaaccc" -// problem2("Aabb") => "bbaA" -// problem2("z") => "z" -// problem2("bookkeeper") => "eeekkoobpr" -// problem2("mississippi") => "iiiissssppm" -function problem2(str) { - // your code -} - -// ---- problem3 ---- -// problem3([1, 1, 2, 2, 2, 3]) => [3, 1, 1, 2, 2, 2] -// problem3([2, 3, 1, 3, 2]) => [1, 3, 3, 2, 2] -// problem3([-1, 1, -6, 4, 5, -6, 1, 4, 1]) => [5, -1, 4, 4, -6, -6, 1, 1, 1] -// problem3([9]) => [9] -// problem3([5, 5, 4, 4]) => [5, 5, 4, 4] -function problem3(arr) { - // your code -} - -// ---- problem4 ---- -// problem4("leetcode") => 0 -// problem4("loveleetcode") => 2 -// problem4("aabb") => -1 -// problem4("x") => 0 -// problem4("aabbc") => 4 -// problem4("aa") => -1 -function problem4(str) { - // your code -} - -// ---- problem5 ---- -// problem5("anagram", "nagaram") => true -// problem5("rat", "car") => false -// problem5("a", "ab") => false -// problem5("", "") => true -// problem5("aacc", "ccac") => false -// problem5("listen", "silent") => true -function problem5(s, t) { - // your code -} - -// ---- problem6 ---- -// problem6([1, 1, 1, 2, 2, 3], 2) => [1, 2] -// problem6([1], 1) => [1] -// problem6([4, 4, 4, 6, 6, 7, 7, 7, 7], 2) => [7, 4] -// problem6([5, 5, 5, 5], 1) => [5] -// problem6([3, 0, 1, 0], 1) => [0] -// problem6([2, 2, 3, 3, 1], 3) => [2, 3, 1] -function problem6(arr, k) { - // your code -} - -// ---- harness: uncomment per problem after implementing ---- -// console.log(problem1([8, 1, 2, 2, 3]), problem1([4, 1, 4, 1])); -// console.log(problem2("tree"), problem2("bookkeeper")); -// console.log(problem3([2, 3, 1, 3, 2]), problem3([5, 5, 4, 4])); -// console.log(problem4("loveleetcode"), problem4("aabbc")); -// console.log(problem5("anagram", "nagaram"), problem5("aacc", "ccac")); -// console.log(problem6([4, 4, 4, 6, 6, 7, 7, 7, 7], 2), problem6([2, 2, 3, 3, 1], 3)); diff --git a/work/Blind/patterns.js b/work/Blind/patterns.js deleted file mode 100644 index 7190bd9..0000000 --- a/work/Blind/patterns.js +++ /dev/null @@ -1,211 +0,0 @@ -// ============================================================ -// PART A FINAL DRILL — Tuesday morning, before 11:30am. -// 8 snippets covering the most likely categories: -// nested loops + string accumulation, reference vs copy, -// sort() defaults, slice/immutability/off-by-one, -// Object.keys strings, splice-while-iterating, -// hidden-space parity, string coercion. -// -// RULES (same as the real thing): -// 1. No paper. No running the code until AFTER you answer. -// 2. For each snippet, deliver the full narration OUT LOUD: -// signature -> structures -> trace -> pattern name -> EXACT output. -// 3. Then scroll to the ANSWER KEY at the bottom and check. -// 4. Log any miss against the gotcha taxonomy. -// -// Target: < 90 seconds per snippet. Do NOT peek early. -// ============================================================ - - -// ---- SNIPPET 1: nested loop + string accumulation ---- -function s1(str) { - let out = ""; - for (let i = str.length - 1; i >= 0; i--) { - for (let j = 0; j < i; j++) out += "*"; - out += str[i]; - } - return out; -} -// console.log(s1("abc")); - - -// ---- SNIPPET 2: reference vs shallow copy ---- -function s2() { - const a = [1, 2, 3]; - const b = a; - const c = [...a]; - b.push(4); - c.push(5); - return [a.length, b.length, c.length]; -} -// console.log(s2()); - - -// ---- SNIPPET 3: sort() default behavior ---- -function s3(arr) { - arr.sort(); - return arr; -} -// console.log(s3([5, 100, 25, 3])); - - -// ---- SNIPPET 4: slice + string immutability + off-by-one ---- -function s4(str) { - str.slice(0, 3); - const tail = str.slice(-2); - return tail + str.slice(1, 2); -} -// console.log(s4("planet")); - - -// ---- SNIPPET 5: Object.keys returns strings ---- -function s5(nums) { - const freq = {}; - for (let n of nums) freq[n] = (freq[n] || 0) + 1; - const keys = Object.keys(freq); - return keys[0] + keys[1]; -} -// console.log(s5([9, 9, 30, 30])); - - -// ---- SNIPPET 6: splice while iterating ---- -function s6(arr) { - for (let i = 0; i < arr.length; i++) { - if (arr[i] < 0) arr.splice(i, 1); - } - return arr; -} -// console.log(s6([-1, -2, 3, -4, -5])); - - -// ---- SNIPPET 7: hidden character parity ---- -function s7(str) { - let out = ""; - for (let i = 0; i < str.length; i++) { - out += i % 2 === 0 ? str[i] : "_"; - } - return out; -} -// console.log(s7("go far")); - - -// ---- SNIPPET 8: string coercion mid-loop ---- -function s8(arr) { - let total = 0; - for (let x of arr) total += x; - return total; -} -// console.log(s8([1, 2, "3", 4])); - - -// ============================================================ -// ============================================================ -// -// S T O P. -// -// Narrate all 8 out loud first. Exact outputs stated. -// Then read the answer key below and check yourself. -// -// ============================================================ -// ============================================================ - - -// ---- ANSWER KEY ---- -// -// SNIPPET 1 -> "**c*ba" -// Loop runs BACKWARDS (i from 2 down to 0). Each pass pads -// i stars, then appends str[i]: -// i=2: "**" + "c" -> "**c" -// i=1: "*" + "b" -> "**c*b" -// i=0: (no stars) + "a" -> "**c*ba" -// Traps: reverse iteration direction + star count comes from -// the INDEX, not the character. Pattern name: "reverse walk -// with index-sized padding." -// Narration line: "The outer loop descends, so the last -// character is emitted first." -// -// SNIPPET 2 -> [4, 4, 4] -// b = a is an ALIAS (same array). c = [...a] is a real copy -// taken BEFORE the pushes. b.push(4) grows a AND b to 4. -// c was [1,2,3], its own push(5) grows it to 4. -// All three read 4 — for two different reasons. If you said -// [4, 4, 5] you forgot c was copied before b.push. If you -// said [3, 4, 4] you missed the alias. -// Narration line: "Assignment aliases; spread copies — and -// the copy freezes the state at the moment it's taken." -// -// SNIPPET 3 -> [100, 25, 3, 5] -// Default sort converts to STRINGS: "100" < "25" < "3" < "5" -// lexicographically (compares char by char: "1" < "2" < "3" -// < "5"). Also note: sort() MUTATES arr in place and returns -// the same reference. -// Narration line: "No comparator, so JavaScript sorts these -// as strings — 100 comes first because the character '1' is -// smallest." -// -// SNIPPET 4 -> "etl" -// Line 1 is a DECOY: strings are immutable and the slice -// result is thrown away — str is unchanged. -// str.slice(-2) = last two chars = "et". -// str.slice(1, 2) = index 1 only (end-exclusive) = "l". -// "et" + "l" = "etl". -// Traps: the dead line, negative slice, end-exclusivity. -// Narration line: "The first slice does nothing — the result -// isn't assigned. Strings are immutable." -// -// SNIPPET 5 -> "930" -// freq = {9: 2, 30: 2}. Object.keys returns ["9", "30"] — -// STRINGS, in ascending numeric order (JS orders integer-like -// keys numerically). "9" + "30" is string CONCATENATION, -// not addition: "930". If you said 39, you added numbers -// that were never numbers. -// Narration line: "Object.keys always returns strings, so -// plus means concatenate here." -// -// SNIPPET 6 -> [-2, 3, -5] -// The splice-while-iterating bug, adjacent-negatives flavor: -// i=0: -1 removed, everything shifts left -> [-2, 3, -4, -5] -// ...but i increments to 1, so -2 (now at index 0) -// is SKIPPED. -// i=1: 3, not negative, stays. -// i=2: -4 removed -> [-2, 3, -5]; -5 slides to index 2, -// i increments to 3, past the end. -5 SKIPPED. -// Every removal skips its right neighbor. Fix if asked: -// iterate backwards, or use filter. -// Narration line: "Each splice shifts the array left while i -// still moves right, so the element after every removal gets -// skipped." -// -// SNIPPET 7 -> "g_ _a_" -// "go far" = g(0) o(1) ' '(2) f(3) a(4) r(5). -// Even indices kept, odd replaced with underscore: -// g, _, ' ', _, a, _ -> "g_ _a_" -// The trap: the SPACE sits at an even index, so it's KEPT — -// the middle of the output is underscore-space-underscore, -// which looks wrong but isn't. Spaces are characters. They -// have indices. -// Narration line: "Index 2 is the space and 2 is even, so -// the space survives." -// -// SNIPPET 8 -> "334" -// total starts as NUMBER 0: -// 0 + 1 = 1, 1 + 2 = 3 (still numbers) -// 3 + "3" = "33" (+ with a string CONCATENATES, -// and total is now a STRING) -// "33" + 4 = "334" -// One string element permanently flips the accumulator's -// type. Everything after it concatenates. -// Narration line: "The plus operator concatenates the moment -// either side is a string — and the poison spreads." -// -// ============================================================ -// SCORING: 8/8 = ready. 6-7 = re-trace the misses out loud -// once and you're ready. Same gotcha missed twice = say that -// taxonomy row out loud three times, then stop. -// -// To verify any answer for real: uncomment its console.log -// and run `node partA-final-drill.js`. -// -// After this: cheatsheet once, decision rule out loud, -// close the laptop. 11:30 is yours. -// ============================================================ diff --git a/work/Easy/Hash Table/387.first-unique-character-in-a-string.go b/work/Easy/Hash Table/387.first-unique-character-in-a-string.go new file mode 100644 index 0000000..2de235e --- /dev/null +++ b/work/Easy/Hash Table/387.first-unique-character-in-a-string.go @@ -0,0 +1,47 @@ +/* + * 387. First Unique Character in a String + * Difficulty: Easy + * https://leetcode.com/problems/first-unique-character-in-a-string/ + * + * ────────────────────────────────────────────────── + * + * Given a string s, find the first non-repeating character in it and + * return its index. If it does not exist, return -1. + * + * + * + * Example 1: + * + * Input: s = "leetcode" + * + * Output: 0 + * + * Explanation: + * + * The character 'l' at index 0 is the first character that does not + * occur at any other index. + * + * Example 2: + * + * Input: s = "loveleetcode" + * + * Output: 2 + * + * Example 3: + * + * Input: s = "aabb" + * + * Output: -1 + * + * + * + * Constraints: + * + * • 1 <= s.length <= 10^5 + * + * • s consists of only lowercase English letters. +*/ + +func firstUniqChar(s string) int { + +}