A common way to prepare for placement coding rounds is to open a list of hundreds of problems and start at number one. A month later, a new problem still feels like a blank page.
A better approach: learn a small set of patterns well enough that you recognise them in problems you've never seen. This post gives you a 30-day plan built that way, with a working Python template for each core pattern.
Thirty days won't make you an expert. But it can take you from "I don't know where to start" to "I can usually tell what kind of problem this is."
Before Day 1: are you ready for DSA?
DSA assumes you can already write loops, conditionals and functions comfortably. If a palindrome check or "second largest element" still feels slow, spend a week or two on basic logic first. Our logic building track covers conditions, loops, recursion, arrays and strings in that order, and it's a reasonable warm-up.
Pick one language for the whole month. The examples below use Python because it keeps templates short.
How to spend each day
Aim for about 2 hours:
- 20 min: learn or revise the pattern (read, or trace it by hand on paper).
- 80 min: solve 2–3 problems that use it. Give yourself about 25 minutes per problem before you look at a hint.
- 20 min: write a short note for each problem: what the pattern was, and what clue in the question should have pointed you to it.
That last step matters most: you're learning the clues, not collecting solutions.
Week 1 (Days 1–7): Arrays, strings and hashing
Days 1–2: Array basics, reversing, rotating, and stating the time complexity of everything you write.
Days 3–4: Strings: counting characters, anagrams, palindromes.
Days 5–6: Hash maps and sets: the "have I seen this before?" pattern.
Day 7: Revise and solve 3 mixed problems without notes.
Pattern: hash map lookup. The clue is "find a pair or duplicate," where the brute-force answer is two nested loops.
def two_sum(nums, target):
seen = {} # value -> index
for i, x in enumerate(nums):
if target - x in seen:
return [seen[target - x], i]
seen[x] = i
return []
assert two_sum([2, 7, 11, 15], 9) == [0, 1]
One pass, O(n) time, O(n) extra space. Say that trade-off out loud in an interview.
Week 2 (Days 8–14): Two pointers, sliding window, prefix sums, binary search
Days 8–9: Two pointers on sorted arrays.
Days 10–11: Sliding window (fixed and variable size).
Day 12: Prefix sums.
Day 13: Binary search, including "binary search on the answer."
Day 14: Revision plus one timed set: 3 problems in 75 minutes.
Pattern: two pointers. The clue is a sorted array, or "pairs, triplets, or in-place partitioning."
def pair_with_sum_sorted(nums, target):
lo, hi = 0, len(nums) - 1
while lo < hi:
s = nums[lo] + nums[hi]
if s == target:
return (lo, hi)
if s < target:
lo += 1
else:
hi -= 1
return None
assert pair_with_sum_sorted([1, 3, 4, 6, 9], 10) == (0, 4)
Pattern: variable sliding window. The clue is "longest/shortest contiguous subarray or substring such that…"
def longest_unique_substring(s):
last = {} # char -> last index seen
start = best = 0
for i, ch in enumerate(s):
if ch in last and last[ch] >= start:
start = last[ch] + 1 # shrink window past the repeat
last[ch] = i
best = max(best, i - start + 1)
return best
assert longest_unique_substring("abcabcbb") == 3
Pattern: prefix sum + hash map. The clue is "count subarrays whose sum equals k." Unlike a sliding window, it also works with negative numbers.
def count_subarrays_with_sum(nums, k):
count, running = 0, 0
freq = {0: 1} # prefix sum -> how many times seen
for x in nums:
running += x
count += freq.get(running - k, 0)
freq[running] = freq.get(running, 0) + 1
return count
assert count_subarrays_with_sum([1, 1, 1], 2) == 2
Pattern: binary search. Write one version you trust and reuse it every time:
def lower_bound(nums, target):
"""First index i with nums[i] >= target."""
lo, hi = 0, len(nums)
while lo < hi:
mid = (lo + hi) // 2
if nums[mid] < target:
lo = mid + 1
else:
hi = mid
return lo
assert lower_bound([1, 2, 4, 4, 7], 4) == 2
Week 3 (Days 15–21): Linked lists, stacks, queues, recursion, trees
Days 15–16: Linked lists: reversal, fast/slow pointers (finding the middle, detecting a cycle).
Day 17: Stacks: valid parentheses, monotonic stack ("next greater element").
Day 18: Recursion and backtracking basics (subsets, permutations).
Days 19–20: Binary trees: DFS (pre/in/post-order) and level-order BFS.
Day 21: Revision.
If pointers feel abstract, watch them move: our DSA visualizers let you step through arrays, linked lists, stacks/queues and sorting in the browser. Use them alongside problems, not instead of them.
Pattern: monotonic stack. The clue is "next greater/smaller element" or "how many days until…"
def next_greater(nums):
res = [-1] * len(nums)
stack = [] # indices with no answer yet
for i, x in enumerate(nums):
while stack and nums[stack[-1]] < x:
res[stack.pop()] = x
stack.append(i)
return res
assert next_greater([2, 1, 3, 2, 5]) == [3, 3, 5, 5, -1]
Week 4 (Days 22–30): Graphs, heaps, intro to DP, mocks
Days 22–23: Graphs: adjacency lists, BFS, DFS, counting connected components.
Day 24: Heaps: top-k elements, merging sorted lists (heapq in Python, priority_queue in C++).
Days 25–27: Dynamic programming basics: climbing stairs, house robber, coin change, longest common subsequence.
Days 28–29: Two full mock rounds: 2 problems, 60 minutes, talk out loud as you go.
Day 30: Go back through your notes and redo every problem you marked "needed a hint."
Pattern: BFS for shortest path in an unweighted graph or grid. The clue is "minimum number of steps/moves."
from collections import deque
def shortest_path_grid(grid):
"""Steps from top-left to bottom-right; 0 = open, 1 = wall."""
n, m = len(grid), len(grid[0])
if grid[0][0] or grid[n-1][m-1]:
return -1
q = deque([(0, 0, 0)])
seen = {(0, 0)}
while q:
r, c, d = q.popleft()
if (r, c) == (n - 1, m - 1):
return d
for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
nr, nc = r + dr, c + dc
if 0 <= nr < n and 0 <= nc < m and not grid[nr][nc] and (nr, nc) not in seen:
seen.add((nr, nc))
q.append((nr, nc, d + 1))
return -1
assert shortest_path_grid([[0, 0, 0],
[1, 1, 0],
[0, 0, 0]]) == 4
Pattern: 1-D dynamic programming. The clue is "number of ways" or "min/max cost," where each answer builds on smaller answers.
def coin_change(coins, amount):
INF = float("inf")
dp = [0] + [INF] * amount # dp[a] = fewest coins to make a
for a in range(1, amount + 1):
for c in coins:
if c <= a:
dp[a] = min(dp[a], dp[a - c] + 1)
return dp[amount] if dp[amount] != INF else -1
assert coin_change([1, 2, 5], 11) == 3
For DP, write the recurrence in words first, then code it.
A quick "which pattern?" cheat sheet
| If the problem says… | Try… |
|---|---|
| sorted array, pair/triplet | Two pointers |
| longest/shortest contiguous… | Sliding window |
| count subarrays with sum k | Prefix sum + hash map |
| "seen before", duplicates, pairs | Hash map / set |
| minimum/maximum value that satisfies a condition | Binary search on the answer |
| next greater/smaller | Monotonic stack |
| minimum steps, levels | BFS |
| all combinations/subsets | Backtracking |
| number of ways, min cost | Dynamic programming |
| top k, k-th largest | Heap |
Mistakes to avoid
- Watching solutions instead of struggling. Twenty-five honest minutes teach you more than a ten-minute video.
- Skipping complexity analysis. State it before the interviewer asks.
- Never practising under time pressure. Do one timed set a week from Week 2.
Save the templates above in one file, run it (every assert should pass silently), and add to it as you learn new patterns. By Day 30 it's your revision sheet.
All the best for placements!
— Schoolabe team
Top comments (0)