function twoSum(nums: number[], target: number): number[] {
const seenIndex = new Map<number, number>();
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
if (seenIndex.has(complement)) return [seenIndex.get(complement)!, i];
seenIndex.set(nums[i], i);
}
return [];
}
Time O(n) · one pass, mỗi op Map là O(1) avg
Space O(n) · Map worst case giữ n entries
Sat note: Quên TS ! non-null assertion sau .get(). Naming sum → đổi seenIndex.
🧠 Brainstorm cue: "Find pair satisfying sum/equation?" → hashmap complement. Walk array, for each x check "have I seen target - x?" Insert after check (tránh dùng cùng index).
function isAnagram(s: string, t: string): boolean {
if (s.length !== t.length) return false;
const freq = new Map<string, number>();
for (let i = 0; i < s.length; i++) {
freq.set(s[i], (freq.get(s[i]) ?? 0) + 1);
freq.set(t[i], (freq.get(t[i]) ?? 0) - 1);
}
for (const count of freq.values()) if (count !== 0) return false;
return true;
}
Time O(n) · single loop n iterations
Space O(k) · k = unique chars; ASCII → O(1) bounded, lowercase a-z → O(26)
Verbalize: "If input is lowercase a-z, k ≤ 26 → effectively O(1) space."
🧠 Brainstorm cue: "Compare 2 collections for equivalence?" → 2 cách: (a) Map ±1 balance (1 loop, 1 map), (b) Sort cả 2 và so sánh. Map balance đẹp hơn cho interview vì O(n) vs O(n log n).
function groupAnagrams(strs: string[]): string[][] {
const buckets = new Map<string, string[]>();
for (const str of strs) {
const sortedKey = [...str].sort().join('');
if (!buckets.has(sortedKey)) buckets.set(sortedKey, []);
buckets.get(sortedKey)!.push(str);
}
return Array.from(buckets.values());
}
Time O(n · k log k) · n strings × k log k sort cost
Space O(n · k) · buckets giữ n strings, mỗi cái độ dài k
Sat mistake: Nói O(n) / O(k) — THIẾU k và n. Bài 2 chiều phải có 2 biến.
Cụm từ: "n strings × per-string-sort-cost. Comparison-based sort = O(k log k). Total time O(n · k log k). Space O(n · k) vì buckets giữ tất cả n × k chars."
6. Longest Substring No Repeat · Sliding Window 🚨 Sat MISTAKE
Medium
function lengthOfLongestSubstring(s: string): number {
const windowChars = new Set<string>();
let left = 0, res = 0;
for (let right = 0; right < s.length; right++) {
const currentChar = s[right];
while (windowChars.has(currentChar)) {
windowChars.delete(s[left]);
left++;
}
windowChars.add(currentChar);
res = Math.max(res, right - left + 1);
}
return res;
}
Time O(n) AMORTIZED · left pointer monotonic, each char touched ≤ 2×
Space O(min(n, k)) · k = charset; ASCII → O(128) bounded
Sat mistake: Nói O(n²) vì thấy nested while trong for. SAI — pointer monotonic → amortized O(n).
Cụm từ memorize: "Despite nested while in for, left pointer monotonic — only moves forward. Total work = O(n) amortized. Each char touched at most 2 times."
🧠 Brainstorm cue: "Longest/shortest contiguous segment with property X?" → Sliding Window. Right expand, shrink left khi vi phạm constraint. State trong Set/Map track window content. Amortized O(n) khi pointer monotonic.
function isValid(s: string): boolean {
const pairs = new Map<string, string>([
[')', '('], [']', '['], ['}', '{'],
]);
const openStack: string[] = [];
for (const char of s) {
if (pairs.has(char)) {
if (openStack.pop() !== pairs.get(char)) return false; // ← () critical!
} else {
openStack.push(char);
}
}
return openStack.length === 0;
}
Time O(n) · single pass
Space O(n) · worst case "((((..." stack giữ n openers
Sat bug:openStack.pop thiếu () → function reference, không gọi → mọi closing bracket return false. Fixed Mon AM.
Sat space mistake: Nói O(1) — SAI. Stack có thể giữ n items → O(n).
🧠 Brainstorm cue: "Nested/matching structures?" → Stack LIFO. Push opens, pop on close + verify match. Stack rỗng cuối = OK. Map <closer, opener> cho fast lookup.
function maxDepth(root: TreeNode | null): number {
if (root === null) return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
Time O(n) · visit mỗi node 1 lần
Space O(h) · h = height. Balanced O(log n), Skewed O(n) — call stack depth
Sat mistake: Nói O(n²) — SAI. Space của tree DFS phụ thuộc HEIGHT, không phải n × n.
Cụm từ: "Time O(n) — visit each node once. Space O(h) — h is tree height. Balanced: O(log n). Skewed: O(n)."
🧠 Brainstorm cue: "Tree property phụ thuộc subtree results?" → DFS recursion. 2 câu hỏi: (1) Base case? (thường null), (2) Recursive case: ghép kết quả của left + right thế nào? Stack ngầm tự nhớ đường về.
function climbStairs(n: number): number {
if (n <= 2) return n;
let prev2 = 1, prev1 = 2;
for (let i = 3; i <= n; i++) {
const current = prev1 + prev2;
prev2 = prev1;
prev1 = current;
}
return prev1;
}
Time O(n) · single loop từ 3 đến n
Space O(1) · 2 biến cố định (không phải DP array)
Why DP not just Fibonacci? "Fibonacci là RESULT, không phải REASONING. Recurrence: dp[i] = dp[i-1] + dp[i-2] vì bước cuối là 1 hoặc 2 bậc → mutually exclusive cases, cộng lại."
Matrix expo: "Có thể tụt xuống O(log n) time bằng matrix exponentiation — nhưng overkill cho interview."
🧠 Brainstorm cue: "Đếm số cách đạt state X?" hoặc "Min/max value achievable?" → DP. 4 bước: (1) Define state dp[i], (2) Recurrence (bước cuối là gì?), (3) Base case, (4) Direction (bottom-up tabulation hoặc top-down memo). Space optimize nếu chỉ phụ thuộc k giá trị trước.
const arr: number[] = [];
arr.push(x); arr.pop();
arr.length; arr[i];
arr.sort((a, b) => a - b); // numbers!
// Iterate
for (let i = 0; i < arr.length; i++) { }
for (const x of arr) { }
Map (5)
const map = new Map<K, V>();
map.set(key, value);
map.get(key); // returns V | undefined
map.has(key); // boolean
map.delete(key);
// Iterate
for (const [k, v] of map) { }
for (const v of map.values()) { }
Array.from(map.values())
Set (3)
const set = new Set<T>();
set.add(x);
set.has(x); // boolean
set.delete(x);
set.size;
// Common idioms
new Set(arr).size !== arr.length // dup check
[...new Set(arr)] // dedup
Comment honest về uncertainty = professional. Tech lead có thể giúp.
💬 3 Killer Phrases (memorize)
PHRASE 1 · Stuck thinking
"Dạ em đang nghĩ — em thấy bài này có signal là [sorted array / find pair], nên em đang cân nhắc giữa [pattern A] và [pattern B]. Em thử approach [A] trước với brute force, sau đó optimize nếu còn thời gian. Anh/chị thấy có sense không ạ?"
→ Show thinking process = senior signal. Interviewer thường confirm direction hoặc hint nhẹ.
PHRASE 2 · Brute force first
"Dạ em sẽ làm brute force trước với O(n²). Sau đó nếu còn thời gian em sẽ optimize bằng [hashmap / sliding window / sort+two pointers]."
→ Brute force submitted > nothing submitted. Mỗi problem đều có naive solution.
PHRASE 3 · Ask for hint (sau 5-7 phút stuck)
"Dạ anh/chị có thể cho em một hint nhỏ về data structure phù hợp không ạ?"
→ KHÔNG mất điểm. Show: escalate khi stuck, communication, time management.
🚫 TUYỆT ĐỐI KHÔNG xin dùng AI
Lý do: Live coding test SKILL, không phải tool. Xin dùng Claude/GPT = defeats entire purpose. 100% downside, 0% upside.
Bro nghĩ
Tech Lead nhận
"Tôi practical, biết dùng tool"
"Anh này không tự code được"
"Tôi adapt với modern workflow"
"Anh này dependence vào AI"
"Show modern AI usage"
"Production code AI-generated → unmaintainable"
Exception: Nếu tech lead OFFER (rất hiếm) — OK dùng cho lookup syntax / verify approach. NEVER dump full problem.
"Dạ em có thể code bằng JavaScript thay vì TypeScript không ạ? Em mạnh JS hơn, focus logic dễ hơn."
Language
Pros
Cons
TypeScript
Show modern stack
Type errors có thể distract
JavaScript
Cleaner syntax, focus logic
Show ít TS knowledge
📝 Notepad Strategy (bring physical paper)
Mang notepad vào interview. Đây không phải cheat — engineer workflow reality. Tech lead OK.
Cấu trúc notepad:
PAGE 1: Personal note
- Tên interviewers (ghi khi họ giới thiệu)
- 3 questions hỏi back cuối interview
PAGE 2: TS Syntax mini cheatsheet (viết tay 15 syntax trên)
PAGE 3-5: Để trống cho take notes lúc PV
⚠️ KHÔNG: Mang printed LeetCode solutions. CHỈ syntax reference + personal notes.
🛡️ Edge Cases — Verbal Walkthrough
Không cần solve hết edge cases. Mention ra miệng = đủ — show defensive thinking.
"Dạ edge cases em sẽ check: empty input · single element · duplicates · negative numbers · very large input."
Standard checklist applies cho hầu hết problems. Memo + bring up đầu interview.