<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by tales-with-ck on Medium]]></title>
        <description><![CDATA[Stories by tales-with-ck on Medium]]></description>
        <link>https://medium.com/@tales-with-ck?source=rss-6a4c7355f365------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*6KDzx0i_ZVEGG8MxGGsZYw.png</url>
            <title>Stories by tales-with-ck on Medium</title>
            <link>https://medium.com/@tales-with-ck?source=rss-6a4c7355f365------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 22 May 2026 00:17:04 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@tales-with-ck/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[HEAPS /PRIORITY QUEUES]]></title>
            <link>https://medium.com/@tales-with-ck/heaps-priority-queues-26ed569d29d2?source=rss-6a4c7355f365------2</link>
            <guid isPermaLink="false">https://medium.com/p/26ed569d29d2</guid>
            <category><![CDATA[heap]]></category>
            <dc:creator><![CDATA[tales-with-ck]]></dc:creator>
            <pubDate>Thu, 21 May 2026 13:52:51 GMT</pubDate>
            <atom:updated>2026-05-21T13:52:51.615Z</atom:updated>
            <content:encoded><![CDATA[<p>Solving neetcode heap tagged problems.</p><h4>KTH LARGEST ELEMENT IN AN ARRAY</h4><pre># KTH LARGEST ELEMENT IN AN ARRAY<br>&quot;&quot;&quot;<br>You are given an unsorted array of integers and an integer k, return kth largest <br>element in the array. <br><br>Largest in the sorted order. <br><br>nums = [2,3,1,5,4] , k = 2 <br>output : 4 <br><br>&quot;&quot;&quot;<br>import heapq<br>def kth_largest_array(nums, k):<br>    heap = []<br>    for num in nums:<br>      heapq.heappush(heap, num)<br>      if len(heap) &gt; k:<br>          heapq.heappop(heap)<br>    return heap[0]<br><br>&#39;&#39;&#39;<br>heap order - it guarantees is parent &lt; children <br>Smallest element will always be at the top the rest can be in any order <br>nums = [2,3,1,5,4] k = 2 <br>heap = [1,2,3] if &gt; k yes so we pop <br>heap = [2,3]<br>add 4 heap = [2,3,4]  is &gt; k yes hence we pop <br>heap = [3,4]<br>add 5 heap = [3,4,5] &gt; k yes hence we pop <br>heap = [4,5] <br>Then we return the top of heap[0]<br>= 4 <br>&#39;&#39;&#39;<br># sorted <br>def kth_largest_array(nums, k):<br>    nums.sort()<br>    return nums[len(nums) - k]<br><br><br></pre><h4>KTH LARGEST ELEMENT IN A STREAM</h4><pre># KTH LARGEST ELEMENT IN A STREAM<br><br>&quot;&quot;&quot;<br>Design a class to find the kth largest integer in a stream of values that <br>include dupicates.<br><br>Implement the following methods. <br>Constructor (int k , int [] nums) <br>Int add (int val)<br><br>Input: [&quot;KthLargest&quot;, [3, [1, 2, 3, 3]], <br>&quot;add&quot;, [3], &quot;add&quot;, [5], &quot;add&quot;, [6], &quot;add&quot;, [7], &quot;add&quot;, [8]]<br>k = 3 <br>stream = [1,2,3,3]<br>So we first push the first ones and then we will pop the extra ones<br>init <br>push 1 - [1]<br>push 2 - [2]<br>push 3 - [3]<br>push 3 - [3] size &gt; k hence we pop the smallest and add it <br>stream = [2,3,3]<br><br>add (3) - size &gt; k, hence we pop smallest [3,3,3] - return 3 <br>add (5) - size &gt; k, hence we pop smallest [3,3,5] - return 3 <br>add (6) - size &gt; k, hence we pop smallest [3,5,6] - return 3 <br>add (7) - size &gt; k, hence we pop smallest [5,6,7] - return 5 <br>add (8) - size &gt; k, hence we pop smallest [6,7,8] - return 6 <br>Hence our output is [null,3,3,3, 5,6]<br><br>Assumptions<br>- Can nums be empty <br>- Can k be larger than nums length <br>- Are duplicates counted by position or uniqueness<br>- -ve numbers possible?<br>- Will there be atleast k elements before add ()<br><br>&quot;&quot;&quot;<br># SORTING <br>k = k <br>arr = nums <br><br>def add(val):<br>    arr.append(val)<br>    arr.sort()<br>    return arr[len(arr) - k] <br><br>class KthLargest:<br>    def __init__(self, k, nums):<br>        self.k = k<br>        self.arr = nums<br><br>    def add(self, val):<br>        self.arr.append(val)<br>        self.arr.sort()<br>        return self.arr[len(self.arr) - self.k]<br><br># HEAP APPROACH<br><br>import heapq <br>class Kth Largest:<br>    def __init__(self, k, nums):<br>        self.k = k<br>        self.heap = []<br>        <br>        for num in nums:<br>            heapq.heappush(self.heap, num)<br>            if len(self.heap)&gt; k:<br>                heap.heappop(self.heap)<br>        def add (self, val):<br>            heapq.heappush(self.heap, val)<br>            if len(self.heap) &gt; self.k:<br>                heapq.heappop(self.heap)<br>            if len(self.heap)&lt; k:<br>                return -1 <br><br>        return self.heap[0]<br><br>    <br><br># FIND MEDIAN FROM DATA STREAM <br># SORTING APPROACH <br>data = []<br><br>def addNum(num):<br>    data.append(num)<br>  <br>def findMedian():<br>    data.sort()<br>    mid = len(data)//2  # it will give me the index not the actual value <br>    if len(data) % 2 != 0:<br>        return float(data[mid]  # odd <br>    else:<br>        return (data[mid-1] + data[mid])/2  # even <br>&#39;&#39;&#39;<br>data = []<br>addNum(1) - data = [1]<br>addNum(2) - data = [1,2]<br>addNum(3) - data = [1,2,3] <br><br>findMedian():<br>sorted = [1,2,3]<br>mid = 3//2 = 1 <br>odd - return data[1] = 2 <br><br>addNum(4) - data = [1,2,3,4] <br>findMedian():<br>sort = [1,2,3,4]<br>mid = 4//2 = 2 <br>even - return data[1] +data[2]/2 = 2 +3/2 = 2.5 <br>&#39;&#39;&#39;<br>class MedianFinder:<br>    def __init__(self):<br>        self.data = []<br>    <br>    def addNum(self, num):<br>        self.data.append(num)               # O(1) - just append<br>    <br>    def findMedian(self):<br>        self.data.sort()                    # O(n log n) - sort only when needed<br>        mid = len(self.data) // 2<br>        if len(self.data) % 2 != 0:<br>            return float(self.data[mid])            # odd - middle element<br>        return (self.data[mid-1] + self.data[mid]) / 2  # even - average<br><br># HEAPS APPROACH <br>import heapq<br>class MedianFinder:<br>    def __init__(self):<br>        self.left = []<br>        self.right = []<br><br>    def add(self, num):<br>        # always push to left first<br>        heapq.heappush(self.left, -num)<br>        <br>        # fix order: left top must &lt;= right top<br>        if self.left and self.right and (-self.left[0] &gt; self.right[0]):<br>            heapq.heappush(self.right, -heapq.heappop(self.left))<br>        <br>        # fix sizes<br>        if len(self.left) &gt; len(self.right) + 1:   # left too big<br>            heapq.heappush(self.right, -heapq.heappop(self.left))<br>        if len(self.right) &gt; len(self.left):        # right too big<br>            heapq.heappush(self.left, -heapq.heappop(self.right))<br>        <br>        return self.get_median()<br>    <br>    def get_median(self):<br>        if len(self.left) &gt; len(self.right):<br>            return float(-self.left[0])             # odd — left top<br>        return (-self.left[0] + self.right[0]) / 2 # even — average<br><br><br># MK AVERAGE IN A STREAM  <br>class MKAverage:<br>    def __init__(self, m, k):<br>        self.m = m<br>        self.k = k<br>        self.stream = []<br>    <br>    def addElement(self, num):<br>        self.stream.append(num)             # just append, sort later<br>    <br>    def calculateMKAverage(self):<br>        if len(self.stream) &lt; self.m:<br>            return -1                       # not enough elements yet<br>        <br>        last_m = self.stream[-self.m:]      # take last m elements<br>        last_m.sort()                       # sort them<br>        trimmed = last_m[self.k:-self.k]    # remove k from each end<br>        <br>        return sum(trimmed) // len(trimmed) # average of middle<br>&#39;&#39;&#39;<br>m=6, k=2<br>obj = MKAverage(6, 2)<br><br>addElement(3)  → stream=[3]<br>addElement(1)  → stream=[3,1]<br>addElement(4)  → stream=[3,1,4]<br>addElement(1)  → stream=[3,1,4,1]<br>addElement(5)  → stream=[3,1,4,1,5]<br>addElement(9)  → stream=[3,1,4,1,5,9]<br><br>calculateMKAverage():<br>len=6 == m continue<br><br>last_m   = [3,1,4,1,5,9]<br>sorted   = [1,1,3,4,5,9]<br>trimmed  = [1,1,3,4,5,9][2:-2]<br>         = [3,4]<br>average  = (3+4)//2 = 3 <br><br>addElement(2) → stream=[3,1,4,1,5,9,2]<br><br>calculateMKAverage():<br>last_m   = [1,4,1,5,9,2]    - last 6 only, 3 dropped off<br>sorted   = [1,1,2,4,5,9]<br>trimmed  = [1,1,2,4,5,9][2:-2]<br>         = [2,4]<br>average  = (2+4)//2 = 3 <br>&#39;&#39;&#39;</pre><h4>LAST STONE WEIGHT</h4><pre>&quot;&quot;&quot;<br>      Intuition:<br>      - Iterate through the array of stones<br>      - At each step pick the two heaviest stones (maxHeap)<br>          - if x == y:<br>              destroy both stones<br>          - if x &lt; y:<br>              destroy x<br>              y = y -x<br>      <br>      stones = [2,3,6,2,4]<br><br>      =&gt; [2, 3, 2, 2]<br>      =&gt; [1, 2, 2]<br>      =&gt; [1]<br><br>&quot;&quot;&quot;<br>import heapq<br> def lastStoneWeight(self, stones: List[int]) -&gt; int:<br>      # Create max heap from stones<br>      heapq.heapify_max(stones)<br>      # Run simulation<br>      while len(stones) &gt; 1:<br>          x = heapq.heappop_max(stones)<br>          y = heapq.heappop_max(stones)<br>          if x == y:<br>              continue <br>          z = x - y<br>          heapq.heappush_max(stones, z)<br>      if not stones:<br>          return 0 <br>      return stones[0]</pre><p>K CLOSEST POINTS TO ORIGIN</p><pre>import heapq<br>from math import sqrt<br>&quot;&quot;&quot;<br>    Assumptions:<br>    - Return in any order<br><br>    Intuition:<br>    - Find all the euclidean distances of the points<br>    - Keep the points in minheap<br>    - Return the k closest points to the origin<br><br>    points = [[0,2],[2,2]], k = 1<br>    minHeap = [ [8, 2, 2]]<br>    res = [[0, 2]]<br><br>    Time: O(n)<br>    Space: O(n)<br><br>    &quot;&quot;&quot;<br><br>def kClosest(points, k):<br>    minHeap = []<br>    res = []<br><br>    for (x, y) in points:<br>        z = sqrt((x ** 2) + (y ** 2))<br>        heapq.heappush(minHeap, [z, x, y])<br>    <br>    while len(res) &lt; k:<br>        (_, x, y) = heapq.heappop(minHeap)<br>        res.append([x, y])<br>    <br>    return res</pre><h4>TASK SCHEDULER</h4><pre>from collections import Counter, deque<br>import heapq<br><br>def leastInterval(tasks, n):<br>    # step 1 — count frequencies<br>    count = Counter(tasks)<br>    <br>    # step 2 — max heap using negation<br>    maxHeap = [-cnt for cnt in count.values()]<br>    heapq.heapify(maxHeap)<br>    <br>    time = 0<br>    q = deque()     # [count, readyTime]<br>    <br>    while maxHeap or q:<br>        time += 1<br>        <br>        if not maxHeap:<br>            # heap empty — jump straight to next ready task<br>            time = q[0][1]<br>        else:<br>            # run most frequent task<br>            cnt = 1 + heapq.heappop(maxHeap)   # decrement (remember negated)<br>            if cnt:                             # still has remaining count<br>                q.append([cnt, time + n])       # push to cooldown queue<br>        <br>        # check if front of queue is ready<br>        if q and q[0][1] == time:<br>            heapq.heappush(maxHeap, q.popleft()[0])<br>    <br>    return time</pre><h4>DESIGN TWITTER</h4><pre>class Twitter:<br><br>    def __init__(self):<br>        self.time = 0<br>        self.followMap = defaultdict(set)<br>        self.tweetMap = defaultdict(list)<br><br>    def postTweet(self, userId: int, tweetId: int) -&gt; None:<br>        self.tweetMap[userId].append((self.time, tweetId))<br>        self.time += 1<br><br>    def getNewsFeed(self, userId: int) -&gt; List[int]:<br>        feed = self.tweetMap[userId][:]<br>        for followeeId in self.followMap[userId]:<br>            feed.extend(self.tweetMap[followeeId])<br><br>        feed.sort(key=lambda x: -x[0])<br>        return [tweetId for _, tweetId in feed[:10]]<br><br>    def follow(self, followerId: int, followeeId: int) -&gt; None:<br>        if followerId != followeeId:<br>            self.followMap[followerId].add(followeeId)<br><br>    def unfollow(self, followerId: int, followeeId: int) -&gt; None:<br>        self.followMap[followerId].discard(followeeId)</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=26ed569d29d2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[DYNAMIC PROGRAMMING 2]]></title>
            <link>https://medium.com/@tales-with-ck/dynamic-programming-2-479dfb95b34a?source=rss-6a4c7355f365------2</link>
            <guid isPermaLink="false">https://medium.com/p/479dfb95b34a</guid>
            <category><![CDATA[dynamic-programming]]></category>
            <dc:creator><![CDATA[tales-with-ck]]></dc:creator>
            <pubDate>Tue, 05 May 2026 12:35:20 GMT</pubDate>
            <atom:updated>2026-05-05T12:35:20.504Z</atom:updated>
            <content:encoded><![CDATA[<p>Lets solve the remaining problems.</p><h4>Decode Ways</h4><pre>A string consisting of uppercase english characters can be encoded<br>to a number using the following mapping:<br>&#39;A&#39; -&gt; &quot;1&quot;<br>&#39;B&#39; -&gt; &quot;2&quot;<br>...<br>&#39;Z&#39; -&gt; &quot;26&quot;<br><br>s= &#39;12&#39;<br>output = 2 <br>&#39;12&#39; could be decoded as &#39;AB&#39; (1 2) or &#39;L&#39; (12)<br><br>s = &#39;01&#39;<br>output = 0 <br>it cannot be decoded because &#39;01&#39; cannot be mapped into a letter.<br><br>Assumptions <br>- What happens when the string is empty we return 0 <br>- What happens if the string starts with 0? we return 0 since its invalid <br>- What if string has consecutive zeros? return 0 since its invalid as well <br>- Are we working with strings or them as values<br>- They are valid range is 1-26 only <br><br>Define the states <br>ways(i) = number of wats to decode s starting at index i <br>s = &#39;126&#39;<br>     012<br><br>Define the recurrence <br>At each index i we have 2 choices <br>- decode single digit - s[i] <br>- decode two digits - s[i + i +2] <br><br>ways(i) = ways(i+1) + ways(i+2)<br><br>Time : O(n) <br>Space : O(n)</pre><pre>def decode_ways(s):<br>  memo = {}<br>  def ways(i):<br>    # base cases <br>    if i == len(s): # decode entire string <br>      return 1 <br>    if s[i] == &#39;0&#39;: # invalid <br>      return 0 <br>    <br>    # memo check <br>    if i in memo:<br>      return memo[i]<br>    #decode single digit <br>    memo[i] = ways[i+1] <br><br>    # decode 2 digits if valid <br>    if i+1 &lt; len(s) and 10 &lt;= int(s[i+i+2]) &lt;= 26 <br>      memo[i] += ways(i +2)<br>    return memo[i]<br>  <br>  return ways(0)<br><br>&#39;&#39;&#39;<br>ways(0) is called<br>    is 0 == len(s)? no<br>    is s[0] == &#39;0&#39;? no, s[0] = &#39;1&#39;<br>    is 0 in memo? no<br><br>    choice 1: single digit<br>      memo[0] = ways(1)<br><br>        ways(1) is called<br>          is 1 == len(s)? no<br>          is s[1] == &#39;0&#39;? no, s[1] = &#39;2&#39;<br>          is 1 in memo? no<br><br>          choice 1: single digit<br>            memo[1] = ways(2)<br><br>              ways(2) is called<br>                is 2 == len(s)? YES - base case<br>                return 1 <br><br>            memo[1] = 1<br><br>          choice 2: two digits<br>            is i+1 &lt; len(s)? 2 &lt; 2? NO skip<br><br>          return memo[1] = 1 <br><br>    memo[0] = 1<br><br>    choice 2: two digits<br>      is i+1 &lt; len(s)? 1 &lt; 2? YES <br>      is 10 &lt;= int(&quot;12&quot;) &lt;= 26? YES <br>      memo[0] += ways(2)<br>              =  1 + 1 = 2 <br><br>    return memo[0] = 2 <br>&#39;&#39;&#39;</pre><h4>Word Break</h4><pre>Given a string s and a dictonary of strings wordDict , return true if s<br>can be segemented into a space - separated sequence of dictonary words. <br><br>You are allowed to reuse words in the dict an unlimited times. <br>Assume all words are unique. <br><br>s= &#39;neetcode&#39; , wordDict = [&#39;neet&#39;, &#39;code&#39;]<br>output = true <br><br>s = &quot;catsincars&quot;, wordDict = [&quot;cats&quot;,&quot;cat&quot;,&quot;sin&quot;,&quot;in&quot;,&quot;car&quot;]<br>Output: false<br><br><strong>Assumptions</strong><br>- What happens when s in empty? return True <br>- What if wordDict is empty we return false <br>- Case sensitive? upper and lower case <br>- Can we reuse words - yes we can <br>- Will all the words be unique? yes <br><br><strong>Define the states </strong><br>segment(i) = can we segment s[i:] into valid dictonary words? <br>s = &#39;neetcode&#39;<br>     01234567<br>i tracks current position in s <br><br>Define the recurrence <br>At each index i we try every word in wordDict:<br>  if s[i + i + len(word)] == word : # word matches at position i <br>    segement(i) = segement(i + len(word)) # skip past the word <br><br> segment(i) = any(segment(i + len(word))<br>               for word in wordDict<br>               if s[i: i + len(word)] == word<br>               <br>Base cases <br>if i == len(s):<br>  return True <br><br><br>Time : O(n)<br>Space : O(n)</pre><pre>def word_break(s, wordDict):<br>    memo = {}<br>    def segment(i):<br>    # Base case <br>    if i == len(s):<br>      return True <br>    # memo check <br>    if i in memo:<br>      return memo[i]<br>    memo[i] = False  # no match found <br>    for word in wordDict:<br>        end = i + len(word) # where the word would end <br>        if s[i:end] == word: # word matches at position i <br>            if segment(end): # can we segment the rest <br>                memo[i] = True # found a valid path <br>                break   # no need to check further <br>        return memo[i]<br><br>    return segment(0)</pre><h4>Unique paths</h4><pre>There is an m x n grid where you are allowed to move either down or<br>to the right at any point in time. <br><br>Return the number of possible unique paths that can be taken from the <br>top-left corner of the grid (grid[0][0]) to the bottom right corner <br>(grid[m-1][n-1] <br><br>input : m = 3 , n = 6 <br>output : 21 <br><br>input: m = 3 , n = 3 <br>output : 6 <br><br>Assumptions <br>- What if m or n is 1? only one path , go straight <br>- What if m or n is 0? return 0 <br>- Can we only move right or down ?<br>- What if m == n == 1 ? already at destination hence return 1 <br><br><strong>Define the states</strong><br>paths(row, col) = no of unique paths from (row, col)<br>                  to bottom right corner (m-1, n-1)<br>grid :<br>0    1    2    3    4    5<br>0  [start  →   →   →   →   →  ]<br>1  [  ↓                       ]<br>2  [  ↓    →   →   →   →  end ]<br><br><strong>Define the recurrence</strong> <br>At each cell we have 2 choices:<br>  - Choice 1 : move right - paths(r, col +1)<br>  - Choice 2 : move down - paths(row+1, c)<br>paths(row,col) = paths(row, col+1) + paths(row+1, col)<br><br><strong>Base cases</strong> <br>if row == m or col == n: # out of bounds <br>  return 0 <br>if row == m-1 and col == n -1: # reached destination<br>  return 1 <br><br>Time: <br>Space:<br></pre><pre>def unique_paths(m,n):<br>    memo = {}<br>    def paths(row, cols):<br>        # Base case <br>        if row == m or col == n:<br>          return 0 <br>        if row == m-1 and col == n-1:<br>          return 1<br>        # Memo check <br>        if (row, col) in memo:<br>           return memo[(row,col)]<br>        memo[(row, col)] = paths(row,col+1) + paths(row+1, col)<br>        return memo[(row,col)]<br>     return paths(0,0)</pre><h4>Longest Increasing Path In a Matrix</h4><pre>You are given a 2D grid of integers matrix, where each integer is &gt;= 0 <br>return the length of longest strictly increasing path within matrix. <br>You can either move horizontally or vertically. (right, left, up , down)<br>No diagonal move. <br>row+1 , col (right)<br>row -1, col (left)<br>row, col +1 (down)<br>row, col -1 (up)<br><br>matrix <br>[5,5,3],<br>[2,3,6],<br>[1,1,1]<br>output = 4 <br>The longest increasing path is [1, 2, 3, 6] or [1, 2, 3, 5].<br><br><br></pre><pre>def increasing_path(matrix):<br>    memo = {}<br>    ROWS, COLS = len(matrix), len(matrix[0])<br>    def path(r,c, prevVal):<br>    # base cases <br>        if r &lt;0 or r &gt;= ROWS or c&lt;0 or c&gt;= COLS or matrix[r][c] &lt;= prevVal:<br>          return 0 <br>      # memo check <br>        if (r,c) in memo:<br>          return memo[(r,c)]<br>      # move in all directions <br>        memo[(r,c)] = 1 <br>        for dr, dc in directions:<br>            neighbor = path(r+ dr, c+ dc, matrix[r][c])<br>            memo[(r,c)] = max(memo[(r,c)], 1 + neighbor)<br>        return memo[(r,c)]<br>        <br>    longest = 0 <br>    for r in range(ROWS):<br>      for c in range(COLS):<br>        dfs(longest, path(r,c,-1))<br>    return longest<br>    <br></pre><h4>Partition Equal Subset Sum</h4><pre>You are given an array of +ve integers nums.<br><br>Return true if you can partition the array into 2 subsets, subset1 and subset2 <br>where sum(subset1) == sum(subset2). Otherwise return false <br><br>Input: nums = [1,2,3,4]<br>Output: true<br><br>Input: nums = [1,2,3,4,5]<br>Output: false<br><br>2 choices <br>- Take the current number into the subset <br>- Skip the current number <br><br>We need to keep reducing the target until :<br>  - target becomes 0 <br>  - we have run out of numbers or target becomes -ve <br><br>Define the states <br>I need to output a boolean if we can partition the array into 2 subsets.<br><br>Define the recurrence relation<br><br><br>Base cases <br>if sum(nums) % 2 : return False # if its odd we return False <br><br>Time: O(n)<br>Space: O(n)<br><br><br></pre><pre># Brute force <br>&#39;&#39;&#39;<br>Time: O (2^n)<br>Space: O (n)<br><br>&#39;&#39;&#39;<br>def canPartition(nums):<br>    if sum(nums) % 2:<br>      return False <br>    def dfs(i, target):<br>      if i &gt;= len(nums):<br>        return target == 0 <br>      if target &lt; 0:<br>        return False <br>      return dfs(i+1, target) or dfs(i+1, target - nums[i])<br>    return dfs(0, sum(nums)//2)<br><br># MEMOIZATION <br>def canPartition(self, nums: List[int]) -&gt; bool:<br>    if sum(nums) % 2:<br>        return False<br><br>    target = sum(nums) // 2<br>    memo = {}<br>    <br>    def partition(i, remaining):<br>        # base cases<br>        if remaining == 0:                 <br>            return True<br>        if i == len(nums):                    <br>            return False<br>        if remaining &lt; 0:                    <br>            return False<br>        <br>        # memo check <br>        if (i, remaining) in memo:<br>            return memo[(i, remaining)]<br>        <br>        memo[(i, remaining)] = partition(i+1, remaining - nums[i])  or partition(i+1, remaining)            <br>        return memo[(i, remaining)]<br>    <br>    return partition(0, target)</pre><h4>Target Sum</h4><pre>You are given an array of integers nums and an integer target. <br><br>For each number in the array, you can choose to either add or substract it to <br>a total sum. <br><br>Return the different ways that you can build the expression such that the total<br>sum equals target. <br><br>nums = [2,2,2] , target = 2 <br>output : 3 <br><br>There are 3 different ways to sum the input numbers to get a sum of 2.<br>+2 +2 -2 = 2<br>+2 -2 +2 = 2<br>-2 +2 +2 = 2<br></pre><pre>def target_sum(nums, target):<br>    memo = {}<br>    <br>    def ways(i, remaining):<br>        # base cases<br>        if i == len(nums) and remaining == 0:   # hit target<br>            return 1<br>        if i == len(nums):                      # missed target<br>            return 0<br>        <br>        # memo check<br>        if (i, remaining) in memo:<br>            return memo[(i, remaining)]<br>        <br>        # two choices: add or subtract<br>        memo[(i, remaining)] = (<br>            ways(i+1, remaining - nums[i])      # add nums[i]<br>            +<br>            ways(i+1, remaining + nums[i])      # subtract nums[i]<br>        )<br>        <br>        return memo[(i, remaining)]<br>    <br>    return ways(0, target)</pre><h4>Interleaving String</h4><pre>You are given 3 strings. s1, s2 and s3. Return true if s3 is formed by<br>interleaving s1 and s2 together or false otherwise. <br><br>Assume s1,s2 and s3 consists of lowercase letters. <br><br>Input: s1 = &quot;aaaa&quot;, s2 = &quot;bbbb&quot;, s3 = &quot;aabbbbaa&quot;<br>Output: true<br><br>We can split s1 into [&quot;aa&quot;, &quot;aa&quot;], s2 can remain as &quot;bbbb&quot; and <br>s3 is formed by interleaving [&quot;aa&quot;, &quot;aa&quot;] and &quot;bbbb&quot;.<br><br>Input: s1 = &quot;&quot;, s2 = &quot;&quot;, s3 = &quot;&quot;<br>Output: true<br><br>Input: s1 = &quot;abc&quot;, s2 = &quot;xyz&quot;, s3 = &quot;abxzcy&quot;<br>Output: false<br><br>Time : O(nxm) n - len(s1) and m - len(s2)<br>Space: O(n ) - memo store and call stack<br></pre><pre>def isInterleaving_string(s1,s2,s3):<br>    if len(s1) + len(s2) != len(s3):<br>        return False <br>    memo = {}<br>    def interleave(i,j):<br>      if i == len(s1) and j == len(s2):<br>        return True <br>      # memo check <br>      if (i,j) in memo:<br>        return memo[(i,j)]  <br>      # make the choices <br>      memo[(i,j)] = False  #Asssume no match <br>      if i &lt; len(s1) and s1[i] == s3[i+j]: # match s1<br>        if interleave(i+1, j)<br>          memo[(i,j)] = True <br>          return True <br><br>      if j &lt; len(s2) and s2[j] == s3[i+j]: # match s2 <br>          if interleave(i, j+1):<br>              memo[(i,j)] = True <br>              return True <br>      return memo[(i,j)] <br><br>    return interleave(0,0)<br><br>&#39;&#39;&#39;<br>length check = 4+4 == 8 <br>interleave(0,0) <br>interleave (0,0) is <br>  i =0 == len(s1) and j=0==len(s2) - no <br>  (0,0) in memo? no <br>  s3[0+0] = s3[0] = &#39;a&#39; <br><br>choice 1: s1[0] =&#39;a&#39; == s3[0] = &#39;a&#39;? yes <br>    interleave (1,0) is called <br>      s3[1+0] = s3[1] = &#39;a&#39; <br>   choice 1: s1[1] = &#39;a&#39; == s3[1] = &#39;a&#39; ? yes <br>    interleave (2,0) is called <br>      s3[2+0] = s3[2] = &#39;b&#39; <br>    choice 1 : s1[2] = &#39;a&#39; == s3[2] = &#39;b&#39; ? no <br>    choice 2 : s2[0] = &#39;b&#39; == s3[2] = &#39;b&#39; ? yes <br>        interleave (2,1) is called <br>        s3[2+1] = s3[3] = &#39;b&#39; <br>      choice 1 : s1[2] = &#39;a&#39; == s3[4] = &#39;b&#39; ? no <br>      choice 2 : s2[2] = &#39;b&#39; == s3[4] = &#39;b&#39; ? yes <br>        interleave(2,3) is called <br>        s3[2+3] = s3[5] = &#39;b&#39; <br>     choice 1 : s1[2]= &#39;a&#39; == s3[5] = &#39;b&#39; ? yes <br>       interleave (2,4) is called <br>       s3[2+4] = s3[6] = &#39;a&#39;<br>       j = 4 == len(s2) so skip choice 2 <br><br>    choice 1 : s1[2] = &#39;a&#39; == s3[6] = &#39;a&#39; ? yes <br>      interleave (3,4) is called <br>      s3[3+4] = s3[7] = &#39;a&#39;<br>      j = 4 == len(s2) so skip choice 2 <br>     <br>    choice 1: s1[3]= &#39;a&#39; == s3[7] = &#39;a&#39;? yes <br>      interleave (4,4) is called <br>      i == len(s1) and j==len(s2) ? yes <br>      return True <br>    <br>      memo[(3,4)] = True : return True <br>      memo[(2,4)] = True  : return True <br>    <br>    memo[(2,3)] = True <br>    memo[(2,2)] = True <br>    memo[(2,1)] = True <br>    memo[(2,0)] = True <br>    memo[(1,0)] = True <br><br>memo[(0,0)] = True <br>True <br>&#39;aa&#39; +&#39;bbbb&#39; + &#39;aa&#39;<br>&#39;&#39;&#39;<br><br>def interleaving_string(s1, s2, s3):<br>    if len(s1) + len(s2) != len(s3):<br>        return False<br>    <br>    memo = {}<br>    <br>    def interleave(s1_idx, s2_idx, s3_idx):<br>        # base case: used all of s3<br>        if s3_idx == len(s3):<br>            return s1_idx == len(s1) and s2_idx == len(s2)<br>        <br>        # memo check<br>        if (s1_idx, s2_idx) in memo:<br>            return memo[(s1_idx, s2_idx)]<br>        <br>        result = False<br>        <br>        # choice 1: match s1<br>        if s1_idx &lt; len(s1) and s1[s1_idx] == s3[s3_idx]:<br>            result = interleave(s1_idx+1, s2_idx, s3_idx+1)<br>        <br>        # choice 2: match s2 only if s1 failed<br>        if not result and s2_idx &lt; len(s2) and s2[s2_idx] == s3[s3_idx]:<br>            result = interleave(s1_idx, s2_idx+1, s3_idx+1)<br>        <br>        memo[(s1_idx, s2_idx)] = result<br>        return result<br>    <br>    return interleave(0, 0, 0)</pre><h4>Edit Distance</h4><pre>You are given 2 strings word1 and word2, each consisting of lowercase <br>english letters. <br>You are allowed to perform 3 operations on word1 an unlimited number of<br>times.   <br>  - Insert a character at any position <br>  - Delete a character at any position<br>  - Replace a character at any position<br>Return the min number of operations to make word1 equal word2<br><br>Input: word1 = &quot;monkeys&quot;, word2 = &quot;money&quot;<br>Output: 2<br>monkeys -&gt; monkey (remove s)<br>monkey -&gt; money (remove k)<br><br>Input: word1 = &quot;neatcdee&quot;, word2 = &quot;neetcode&quot;<br>Output: 3<br>neatcdee -&gt; neetcdee (replace a with e)<br>neetcdee -&gt; neetcde (remove last e)<br>neetcde -&gt; neetcode (insert o)<br><br>Time : O(m x n) <br>Space: O(m x n)<br></pre><pre># Brute force approach <br>def minDistance(word1, word2):<br>    m, n = len(word1), len(word2)<br>    def edit(i, j):<br>      if i == m:<br>          return n-j<br>      if j == n:<br>          return m-i <br>      if word1[i] == word2[j]:<br>          return edit(i+1, j +1)<br>      res = min(edit(i+1, j) , edit(i, j+1))<br>      res = min(res, edit(i+1, j+1))<br>      return res +1 <br>    return edit(0,0)<br><br><br># Memoization<br>def minDistance(word1, word2):<br>    m, n = len(word1), len(word2)<br>    memo = {}<br>    def edit(i, j):<br>      if i == m:<br>          return n-j<br>      if j == n:<br>          return m-i<br>      if (i,j) in memo:<br>          return memo[(i,j)] <br>      if word1[i] == word2[j]:<br>          memo[(i,j)] = memo(i+1, j+1)<br>      else:<br>        res = min(edit(i+1, j) , edit(i, j+1))<br>        res = min(res, edit(i+1, j+1))<br>        memo[(i,j)] = res + 1<br>        return memo[(i,j)] <br>    return edit(0,0)<br></pre><h4>Burst Balloons</h4><pre>You are given an array of integers nums of size n. The ith element represents<br>a balloon with an integer value of nums[i]. You must burst all of the balloons. <br><br>If you burst the ith balloon, you will receive nums[i-1] * nums[i] * nums[i+1] coins<br>If i-1 or i+1 goes out of bounds of the array, then assume the out of bounds value is 1.<br><br>Return the maximum no of coins you can receive by busrting all balloons. <br><br>nums = [4,2,3,7]<br>output = 143 <br>Explanation:<br>nums = [4,2,3,7] --&gt; [4,3,7] --&gt; [4,7] --&gt; [7] --&gt; []<br>coins =  4*2*3    +   4*3*7   +  1*4*7  + 1*7*1 = 143</pre><pre>def max_coins(nums):<br>    ballons = [1] + nums +[1]<br>    memo = {}<br>    def burst(left, right):<br>        if left &gt; right:<br>          return 0 <br>        if (left, right) in memo:<br>          return memo[(left, right)]<br>        memo[(left, right)] = 0 <br>        <br>        for last in range(left, right +1):<br>            coins = ballons[left-1] * ballons[last] * ballons[right+1]<br>            coins += burst(left, last-1) + burst(last+1, right)<br>            memo[(left, right)] = max(memo[(left, right)], coins)<br><br>        return memo[(left, right)]<br><br>    return burst(1, len(ballons)-2)</pre><h4>Regular Expression Matching</h4><pre>You are given an input string s consisting of lowercases english letters, and <br>a pattern p consisting of lowercase english letters, as well as &#39;.&#39; and &#39;*&#39; characters.<br><br>Return true if the pattern matches the entire input string, otherwise return Flse<br><br>&#39;.&#39; matches any single character <br>&#39;*&#39; matches 0 or more of the preceding element<br>s = &#39;aa&#39; p = &#39;.b&#39; <br>output = false <br><br>Regardless of which character we choose for the &#39;.&#39; in the pattern, <br>we cannot match the second character in the input string.<br><br>s= &#39;nnn&#39; p = &#39;n*&#39; <br>output = true <br>&#39;*&#39; means zero or more of the preceding element, &#39;n&#39;. We choose &#39;n&#39; to <br>repeat three times.<br><br>s = &quot;xyz&quot;, p = &quot;.*z&quot;<br>Output: true<br>The pattern &quot;.*&quot; means zero or more of any character, so we choose &quot;..&quot; <br>to match &quot;xy&quot; and &quot;z&quot; to match &quot;z&quot;.<br><br>Time: O (n)<br>Space: O(n)</pre><pre># Brute Force approach <br>def isMatch(s,p):<br>    m, n = len(s), len(p)<br><br>        def dfs(i, j):<br>            if j == n:<br>                return i == m<br><br>            match = i &lt; m and (s[i] == p[j] or p[j] == &quot;.&quot;)<br>            if (j + 1) &lt; n and p[j + 1] == &quot;*&quot;:<br>                return (dfs(i, j + 2) or          # don&#39;t use *<br>                       (match and dfs(i + 1, j))) # use *<br>            if match:<br>                return dfs(i + 1, j + 1)<br>            return False<br><br>        return dfs(0, 0)<br>    <br><br><br># Memoization<br>def isMatch(s,p):<br>        m, n = len(s), len(p)<br>        cache = {}<br><br>        def dfs(i, j):<br>            if j == n:<br>                return i == m<br>            if (i, j) in cache:<br>                return cache[(i, j)]<br><br>            match = i &lt; m and (s[i] == p[j] or p[j] == &quot;.&quot;)<br>            if (j + 1) &lt; n and p[j + 1] == &quot;*&quot;:<br>                cache[(i, j)] = (dfs(i, j + 2) or<br>                                (match and dfs(i + 1, j)))<br>                return cache[(i, j)]<br><br>            if match:<br>                cache[(i, j)] = dfs(i + 1, j + 1)<br>                return cache[(i, j)]<br><br>            cache[(i, j)] = False<br>            return False<br><br>        return dfs(0, 0)</pre><h3>Best Time to Buy And Sell Stock With Cooldown</h3><pre>You are given an integer array prices where prices[i] is the price of <br>NeetCoin on the ith day.<br><br>You may buy and sell one NeetCoin multiple times with the following <br>restrictions:<br><br>    After you sell your NeetCoin, you cannot buy another one on the <br>    next day (i.e., there is a cooldown period of one day).<br>    You may only own at most one NeetCoin at a time.<br><br>You may complete as many transactions as you like.<br><br>Return max profit you can achieve. <br>Input: prices = [1,3,4,0,4]<br>Output: 6<br><br>Buy on day 0 (price = 1) and sell on day 1 (price = 3), profit = 3-1 = 2. <br>Then buy on day 3 (price = 0) and sell on day 4 (price = 4), <br>profit = 4-0 = 4. Total profit is 2 + 4 = 6.</pre><pre># Brute Force approach<br>def maxProfit(prices):<br>        def dfs(i, buying):<br>            if i &gt;= len(prices):<br>                return 0<br><br>            cooldown = dfs(i + 1, buying)<br>            if buying:<br>                buy = dfs(i + 1, not buying) - prices[i]<br>                return max(buy, cooldown)<br>            else:<br>                sell = dfs(i + 2, not buying) + prices[i]<br>                return max(sell, cooldown)<br><br>        return dfs(0, True)<br><br># Memoization<br>def maxProfit(prices):<br>        dp = {}  # key=(i, buying) val=max_profit<br><br>        def dfs(i, buying):<br>            if i &gt;= len(prices):<br>                return 0<br>            if (i, buying) in dp:<br>                return dp[(i, buying)]<br><br>            cooldown = dfs(i + 1, buying)<br>            if buying:<br>                buy = dfs(i + 1, not buying) - prices[i]<br>                dp[(i, buying)] = max(buy, cooldown)<br>            else:<br>                sell = dfs(i + 2, not buying) + prices[i]<br>                dp[(i, buying)] = max(sell, cooldown)<br>            return dp[(i, buying)]<br><br>        return dfs(0, True)</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=479dfb95b34a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[DYNAMIC PROGRAMMING]]></title>
            <link>https://medium.com/@tales-with-ck/dynamic-programming-1dfd7c00b1f4?source=rss-6a4c7355f365------2</link>
            <guid isPermaLink="false">https://medium.com/p/1dfd7c00b1f4</guid>
            <category><![CDATA[memoization]]></category>
            <category><![CDATA[recursion]]></category>
            <category><![CDATA[dynamic-programming]]></category>
            <dc:creator><![CDATA[tales-with-ck]]></dc:creator>
            <pubDate>Tue, 28 Apr 2026 12:26:09 GMT</pubDate>
            <atom:updated>2026-04-28T12:26:09.864Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uKlGhPKazDBuUj1v_RlRVw.png" /><figcaption>Dynamic Programming</figcaption></figure><p>Lets solve the neetcode 150 dynamic programming list.</p><p>So when the interviewer start with <strong>number of ways</strong> just know thats a DP problem.</p><p><strong>Basics first.</strong></p><p>So dynamic programming you can solve the problem with a brute force approach (recursion), memoization or using tabulation.</p><p>My focus will be mostly <strong>memoization </strong>so I can cement the recursion approach. In this approach, we try to solve the bigger problem by recursively finding the solution to smaller sub-problems. Whenever we solve a sub-problem, we cache its result so that we don’t end up solving it repeatedly if it’s called multiple times. Instead, we can just return the saved result.</p><p>When you thinking of solving using dynamic programming you need to first think of a few things.</p><ol><li><strong>Define the states</strong>. It is what uniquely identifies a subproblem<strong>. </strong>What does each DP entry represent? Think what am I trying to compute at each step? Like of the pattern that you need to use.</li></ol><p>Example am I solving using fibonnaci, knapsack, LIS or LCS.</p><ol><li><strong>Define the recurrence</strong>. How do I build the solution for a state using smaller states. Like what is the algorithm that will help me achieve what I need to solve what I am being asked.</li><li>Choose the correct implementation style. How do I actually code it. Do I use brute force, memoization or tabulation.</li></ol><p>So something I have noticed is you can choose to solve the problem using dictionary memoization or array memoization.</p><p>My focus will be mainly dictionary (hashmap) technique to enforce mastery.</p><p>So that you can understand how memoization works</p><pre>memo = empty map {}<br># helper function <br>f(sub_problem_id):<br>  # basecases<br>  if sub_problem is basecase:<br>    return result directly<br>  if subproblem in memo_map:<br>    return cached result <br>  memo[subproblem_id] = recurrence relation formula<br>  return memo[subproblem_id]<br>return f(initial subproblem)</pre><h4>Climbing stairs</h4><pre>You are climbing stairs.<br>It takes n steps to reach the top. <br>Each time you can either climb 1 or 2 steps. <br>(This forms the basecases)<br><br>the 3 ways are:<br>  1+1+1<br>  1+2<br>  2+1<br><br>Define the states<br>- How many ways can I reach step i (current step)<br><br>Define the recurrence <br>- You have 2 choices : 1 or 2 steps<br>- Climb 1 step <br>- Climb 2 steps <br>dfs(i) = dfs(i-1) + dfs(i-2)<br>Hence it resembles fibonnaci sequence <br><br>Basecases<br>if i == n:<br>    return 1 - valid path <br>if i &gt; n:<br>    return 0 - invalid path <br><br><br>We will implement using memoization <br><br>Time : O(n) - memoization ensures each subproblem is solved exactly once.<br>Space: O(n) - for the memo table and O(n) for the recursive call stack</pre><pre># FORWARD APPROACH <br>def climbing_stairs(n):<br>  memo= {}<br>  def dfs(i):<br>    if i == n:<br>      return 1 <br>    if i &gt; n:<br>      return 0 <br>    if i in memo:<br>      return memo[i]<br>    memo[i] = dfs( i + 1) + dfs(i +2)<br>    return memo[i]<br>  return dfs(0) <br><br>&#39;&#39;&#39;<br>dfs(0) = 0 <br>dfs(1) = 1 <br>dfs(2) = 2 <br>dfs(3) = 3 <br>dfs(4) = 5 <br>dfs(5) = 8 <br><br><br><br>n = 3<br>define the function<br>initialize the cache = {}- its empty nothing to store <br>We define the helper function dfs(i)<br>Kick it off with return dfs(0) hence we start ground up <br><br>dfs(0) is called <br>  is 0 == n? no<br>  is 0 &gt; n? no <br>  is 0 in cache ? no <br>  so we compute cache[i] = dfs(1) + dfs(2) - 2 + 1 (both already <br>in memo hence we dont need to recompute them)  = 3 - so we store <br>memo[0] <br><br>  dfs(1) is called<br>    is 1 == n?    no<br>    is 1 &gt; n?     no<br>    is 1 in memo? no<br>    memo[1] = dfs(2) + dfs(3) - 1 + 1 (memo[2] is <br>already stored and dfs(3) hit the base case = 2 - so we store <br>memo[1] then we bubble back to dfs(0)<br><br>  dfs(2) is called<br>    is 2 == n?    no<br>    is 2 &gt; n?     no<br>    is 2 in memo? no<br>    memo[2] = dfs(3) + dfs(4) = 1+0 = 1 - so we store<br>we store memo[2] - we bubble back to dfs(1)<br><br>  dfs(3) is called <br>    is 3 == n ? yes hence we return 1 <br>  dfs(4) is called <br>    is 4 &gt; n? yes hence we return 0 (out of bound)<br><br>  memo[2] = 1 + 0 = 1 <br>  dfs(3) is called <br>    is 3 == n yes we already have this hence we return 1 <br>  memo[1] = 1+1 = 2 <br>  dfs(2) is called <br>    is 2 in memo - yes hence we dont recompute it <br>  memo[0] = 2 + 1 = 3 <br>Hence our final answer is 3 <br>&#39;&#39;&#39;<br><br># BACKWARD APPROACH <br>def climbing_stairs(n)<br>  memo = {}<br>  def climb(step):<br>    if step &lt;= 2:<br>      return step <br>    if step in memo:<br>      return memo[step]<br>    memo[step] = climb(step -1) + climb(step-2)<br>    return memo[step]<br>  return climb(n)<br><br># BRUTE FORCE <br>def climbing_stairs(n):<br>    def dfs(i):<br>        if i == n:      # reached top<br>            return 1<br>        if i &gt; n:       # out of bound<br>            return 0<br>        return dfs(i+1) + dfs(i+2)  # recompute everything<br>    return dfs(0)</pre><h4>Fibonnaci number</h4><pre>Given n , calculate f(n). <br>n = 2 <br>output = 1 <br><br>n = 3 <br>output = 2 <br><br><strong>Define the states </strong><br>- f(i) = the fibonnaci number at position i <br><br><strong>Define the recurrence</strong> <br>f(i) = f(i -1) + f(i -2) - each number is the sume of the 2 numbers <br>before it. <br><br>0, 1,1,2,3,5,8 <br><br><strong>Basecases</strong><br>- 0 and 1 <br><br>We will implement using memoization <br><br>Time: O(n) - each subproblem solved exactly once<br>Space: O(n) - for the memo table and O(n) for the recursive call stack </pre><pre># BACKWARD APPROACH<br>def fib(n):<br>  memo = {}<br>  def dfs(i):<br>    if i == 0:<br>      return 0 <br>    if i == 1:<br>      return 1<br>    if i in memo:<br>      return memo[i]<br>    memo[i] = dfs(i -1) + dfs(i -2)<br>      return memo[i]<br>  return dfs(n)<br><br>&#39;&#39;&#39;<br>Define the function <br>Initialize the memo = empty since we have nothing to store <br>Define the helper function dfs(i)<br>Kick it off with return dfs(n) hence we backwards <br>n = 4<br>dfs(4) is called:<br>  is 4 == 0 ? no <br>  is 4 == 1 ? no <br>  is 4 in memo ? no <br>  memo[4] = dfs(3) + dfs(2) - 2 + 1 = 3 - hence we return it as final <br>answer. <br><br>dfs(3) is called:<br>  is 3 == 0 ? no <br>  is 3 == 1 ? no <br>  is 3 in memo ? no <br>  memo[3] = dfs(1) + dfs(2) - 1 + 1 = 2 - we bubble back up <br><br>dfs(2) is called:<br>  is 2 == 0 ? no <br>  is 2 == 1 ? no <br>  is 2 in memo ? no <br>  memo[2] = dfs(1) + dfs(0) - 1 + 0 = 1 - bubble back up <br><br>dfs(1) is called:<br>  is 1 == 0 ? no <br>  is 1 == 1 ? yes - we have hit a basecase<br><br>dfs(0) is called:<br>  is 0 == 0 ? yes - we have hit a base case <br>&#39;&#39;&#39;<br><br># BRUTE FORCE <br>def fib(n):<br>    def dfs(i):<br>        if i == 0:      # base case<br>            return 0<br>        if i == 1:      # base case<br>            return 1<br>        return dfs(i-1) + dfs(i-2)  # recompute everything<br>    return dfs(n)</pre><h4>Min Cost Climbing Stairs</h4><pre>You are given an array of integers cost where cost[i] is cost of taking a step<br>from ith floor of a staircase. <br>You can step either (i + 1)th floor or (i + 2)th floor. <br>You can choose to start at index 0 or the index 1 floor. <br>Return the minimum cost to reach top of the staircase. <br><br>Example :<br>Input : cost = [1,2,3]<br><br>Output : 2 <br><br>Define the states<br>Minimum cost to reach the top from step i <br><br>At each step we have 2 choices to make to either take 1 or 2 steps<br><br><strong>Define the states</strong><br>We trying to find the minimum cost to reach the top from step i <br><br><strong>Define the recurrence </strong><br>At each step step i you have 2 choices: <br>  - climb 1 step or climb 2 steps <br>dfs(i)  = cost[i] + min(dfs(i +1), dfs(i + 2))<br>cost[i] - pay current step cost, then take the cheaper path.<br><br><br><strong>Basecases</strong> <br>if i &gt;= len(cost): - reached or passed the top<br>  return 0 <br><br><br><br>I will implement using memoization <br><br>Time: O(n) - each subproblem solved exactly once<br>Space: O(n) - for the memo table and O(n) for the recursive call stack</pre><pre>def min_cost_climbing_stairs(cost):<br>  memo = {}<br>  def dfs(i):<br>    if i &gt;= len(cost): # base case , reached the top<br>      return 0 <br>    if i in memo:<br>      return memo[i]<br>    memo[i] = cost[i] + min(dfs(i+1), dfs(i +2)) # pay and take cheaper path<br>    return memo<br>  return min(dfs(0), dfs(1)) # you can start here<br><br>&#39;&#39;&#39;<br>dfs(3)<br>- is 3 &gt;= len(cost) yes - hence return 0 <br>dfs(2) <br>- is 2 in memo ? no <br>memo[2] = cost[2] + min(dfs(3) + dfs(4)<br>         = 3 + min(0, 0) = 3 <br>dfs(1)<br>- is 1 in memo? no <br>memo[1] = cost[1] + min(dfs(2), dfs(3))<br>        = 2 + min(3,0) = 2 <br>dfs(0) <br>- is 0 in memo? no <br>memo[0] = cost[0] + min(dfs(1), dfs(2))<br>        = 1  + min(2,3) = 3 <br>return 3<br>&#39;&#39;&#39;<br><br># BRUTE FORCE <br>def min_cost_climbing(cost):<br>  def dfs(i):<br>    if i &gt;= len(cost):<br>      return 0 <br>    return cost[i] + min(dfs(i + 1), dfs(1 + 2))<br>  return min(dfs(0), dfs(1))</pre><h4>Longest Increasing Subsequence</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*6U0SE8YubV0dO3Jw-6iPJw.png" /><figcaption>Example</figcaption></figure><pre>Given an integer array nums , return the length of longest increasing <br>subsequence.<br>A subsequence - Delete characters while maintaing characters in the sequence. <br>It doesnt have to be contiguous. (They dont have to follow each other).<br><br>Input : nums = [9,1,4,2,3,3,7]<br>output = 4 <br>Values = [1,2,3,7]<br><br>Assumptions to think about and ask your interviewer  <br>- What happens when you have duplicates?<br>- Think about the input size <br>- What happens when you have negative values<br>- What if they need you to return the actual values compared to the total of it<br>- What if you need to return the indexes as well<br>- Think of the edgecases: when its empty, null, happy and unhappy path <br><br>Visual this as a tree <br>nums = [9,1,4,2,3,3,7]<br>        <strong>BRUTE FORCE</strong><br>        at 9 you have 2 choices - to either include it or not include it<br>        when we get to the other value 1 we also have 2 choices<br>        So we do that for each single character and realise it 2 ^ n - exponential<br>        Can we do better?<br>  O (n^2)<br><strong>DFS with cache </strong><br>[1,2,4,3]<br>0  1 2 3<br><br>So we take 1 decision at each index and add their values. Hence this will be the <br>subsequences we have so far. <br><br>I have shared a diagram above to show how it looks like.<br><br>So we know when we start at index 0 we have 3 values that<br>come afterwards. So we can try the 3 possible decisions. <br><br>So they are possible since all the values are greater than<br>1. <br><br>Something to not is that if the values are not greater we can<br>continue there hence we stop. <br><br>LIS[0] = 3 <br>LIS[1] = 2 <br>LIS[2] = 1 <br>LIS[3] = 1 <br><br><strong><br>Define the states - we trying to understand the problem<br></strong>Length of longest increasing subsequence starting at index i <strong><br><br>Define the recurrence <br></strong>At each index i, we look at every j after i <br>If nums[j]&gt; nums[i]: only include if its greater <br>  - we can extend the subsequence <br>dfs(i) = 1 + max(dfs(j)) for all j &gt; i where nums[j] &gt; nums[i] <br>1 represent count of our current element <br><strong><br>Basecases <br></strong>If i == len(nums): return 0 (we out of bound)<br><br><strong>dfs(0) - we can go to indices 1,2,3 (2 &gt; 1 - [1,2], 4 &gt; 1 - [1,4], 3 &gt; 1 - [1,3]<br>dfs(1) - we can go to indices 2,3  (4 &gt; 2 - [1,2,4], 3 &gt; 2 - [1,2,3])<br>dfs(2) - we can go nowhere (nothing after 4 is greater)<br>dfs(3) - we can go nowhere (nothing after 3)<br><br>LIS[3] - 1 <br>LIS[2] - 1 <br>LIS[1] = 1 + max(LIS[2], LIS[3] = 1 + max(1,1) = 2 <br>LIS[0] = 1 + max(LIS[1], LIS[2], LIS[3]) = 1 + max(2,1,1) = 3 <br></strong><br><strong><br></strong>We will implement with memoization<strong><br><br>Time : O (n^2) - Running through i and j <br>Space: O (n) - memoziation and calling the stack each level <br></strong></pre><pre># DICTIONARY MEMOIZATION <br>def longest_increasing_subsequence(nums):<br>    memo = {}<br>    def lis(current):<br>        if current == len(nums):<br>            return 0<br>        if current in memo:<br>            return memo[current]<br>        <br>        memo[current] = 1<br>        for next_idx in range(current + 1, len(nums)):<br>            if nums[next_idx] &gt; nums[current]:<br>                memo[current] = max(memo[current], 1 + lis(next_idx))<br>        return memo[current]<br>    return max(lis(current) for current in range(len(nums)))<br><br>&#39;&#39;&#39;<br>dfs(3) <br>- is 3 in memo? no <br>- memo[3] = 1 <br>- Its out of bound<br>- return 1 <br><br>dfs(2) <br>- is 2 in memo? no <br>- memo[2] = 1 <br>- j = 3 : is num[3] &gt; nums[2] ? is 3 &gt; 4 ? no <br>- return 1 <br><br>dfs(1)<br>- Is 1 in memo ? no <br>- memo[1] = 1 <br>- j = 2 : is nums[2] &gt; nums[1]? is 4 &gt; 2? yes <br>memo[1] = max(1, 1 + dfs(2)) = max(1, 1+1) = 2<br>- j = 3 : is nums[3] &gt; nums[1]? is 3 &gt; 2? yes<br>memo[1] = max(2, 1 + dfs(3)) = max(2, 1+1) = 2 <br>return 2 <br><br>dfs(3) <br>- Is 0 in memo? no <br>- j = 1: is nums[1] &gt; nums[0]? is 2 &gt; 1? yes<br>memo[0] = max(1, 1 + dfs(1)) = max(1, 1+2) = 3<br>- j = 2 : already in memo <br>- j = 3 : already in memo <br>- return 3 <br><br>max(3,2,1,1) = 3 <br>values = [1,2,3] or [1,2,4]<br>&#39;&#39;&#39;<br><br># ARRAY MEMOIZATION <br>def longest_increasing_subsequence(n):<br>  memo = [-1] * n <br>  def dfs(i):<br>    if memo[i] != -1:<br>      return memo[i]<br>    LIS = 1 <br>    for j in range(i + 1, n):<br>      if nums[j] &gt; numsi]:<br>        LIS = max(LIS, 1 + dfs(j))<br>    memo[i] = LIS <br>    return LIS <br>  return max(dfs(i) for i in range(len(nums))</pre><h4>Longest Common Subsequence</h4><pre>Given 2 strings text1 and text2, return the length of their <br>longest common subsequence. If there is no common subsequence<br>return 0. <br><br>Assumptions <br>- What if either string is empty?<br>- What if both strings are identicial ?<br>- What happens if there is no common subsequence?<br>- Is it case sensitive?<br>- What if they want the actual subsequence not just the length?<br>- What if they want indices?<br><br>Example:<br>text1 : &#39;cat&#39; text2: &#39;crabt&#39;<br>output : 3<br><br><strong>Define the states </strong><br>We need to find the longest common subsequence. Starting at index i in s1 and <br>index j in s2 <br><br><strong>Define the recurrence relation</strong><br>We have 2 choices at each i and j <br>Choice 1 : characters match <br>take it and move both pointers forward <br>dfs(i,j) = 1 + dfs(i+1, j+1)<br>Choice 2 : characters dont match <br>try skipping one character from either string <br>dfs(i, j) = max(dfs(1+1, j) , dfs(i, j+1)<br><br><strong>Basecases </strong><br>if i == len(text1): return 0 <br>if j == len(text2) : return 0 <br><br><br>We will implement using memoization<br><br>Visualization of the problem <br><br>     a  c  e<br>     0  1  2<br>a 0 [3, 2, 1]<br>b 1 [2, 2, 1]<br>c 2 [2, 2, 1]<br>d 3 [1, 1, 1]<br>e 4 [1, 1, 1]<br><br>Each cell (i,j) = LCS from i in text1 and j in text2 <br>we fill from bottom right to top left <br></pre><pre>def longestCommonSubsequence(text1, text2):<br>  memo = {}<br>  def count(i,j):<br>    if i == len(text1) or j == len(text2):<br>        return 0 <br>    if (i, j) in memo:<br>        return memo[(i,j)]<br>    if text1[i] == text2[j]:<br>        memo[(i,j)] = 1 + count(i + 1, j + 1)<br>    else:<br>        memo[(i,j)] = max(count(i + 1, j), count(i, j + 1 ))<br>        return memo[(i,j)]<br>  return count(0,0)<br><br>&#39;&#39;&#39;<br>text1 = &#39;abcde&#39;<br>text2 = &#39;ace&#39;<br>we kick it off at dfs(0,0)<br>dfs(0,0)<br>- 0 == len(text1) ? no<br>- 0 == len(text2) ? no<br>- (0,0) in memo? no<br>  text1[0] == text2[0]? a == a yes <br>  memo[(0,0)] = 1 + dfs(1,1) = 1 + 2 = 3 <br><br>dfs(1,1)<br>- (1,1) in memo? no<br>  text1[1] == text2[1]? b == c - no <br>  memo[(1,1)] = max(dfs(2,1), dfs(1,2)) = max(2,1) = 2<br><br>  dfs(1,2) <br>  is (1,2) in memo? no <br>  text1[1] == text2[2] ? b == e ? no <br>  memo[(1,2)] = max(dfs(2,2), dfs(1,3)) = max(1,0) = 1<br>  <br>  dfs(2,2)<br>  is (2,2) in memo? no <br>  text1[2] == text2[2] ? c == e ? no<br>  memo[(2,2)] = max(dfs(3,2), dfs(2,3))<br>            = max(1,0) - dfs(3,2) - already in memo = 1 <br>  <br>  dfs(1,3) <br>  is 3 == len(text2) - yes hence basecase <br>  return 0 <br> <br><br><br>dfs(2,1)<br>- (2,1) in memo? nope<br>  text1[2] == text2[1]? c == c - yes<br>  memo[(2,1)] = 1+ max(dfs(3,2) - 1 +1 = 2 <br><br>dfs(3,2)<br>- (3,2) in memo? nope<br>  text1[3] == text2[2]? d == e - no<br>  memo[(3,2)] = max(dfs(4,2), dfs(3,3)) - max(1,0) = 1 <br>dfs(3,3) == len(text2) hence basecase return 0 <br><br>dfs(4,2)<br>- (4,2) in memo? nope<br>  text1[4] == text2[1]? e == e - yes<br>  memo[(4,2)] = 1+ max(dfs(5,3) - 1 + 0 = 1 <br><br>dfs(5,3) <br>- if 5 == len(text1) = yes hence base case reached return 0 <br><br><br>Final answer = 3 <br>common subsequence = &#39;ace&#39;<br>&#39;&#39;&#39;</pre><h4>Distinct subsequence</h4><pre>You are given 2 strings s and t , both consisting of english letters. <br>Return the number of distinct subsequences of s which are equal<br>to t. <br><br>Input : s = &#39;caaat&#39; t = &#39;cat&#39;<br>output : 3 <br><br>Input : s = &#39;xxyxy&#39; , t = &#39;xy&#39; <br>output: 5 <br><br><strong>Assumptions</strong><br>- We are only working with strings or numbers as well?<br>- Duplicates count as distinct if at different indexes <br>- What is the input size?<br>- Is it case sensitive? focus on lower case letters <br>- Empty string s - return 0 <br>- Empty string t - return 1 <br>- s is shorter than t - return 0 since its impossible <br><br><br><strong>Define the states </strong><br>We need to find the number of distinct subsequences of s which <br>are equal to t. <br> <br>count (i,j) = number of distinct subsequences of s[i:]<br>              that equal t[j:]<br>s = &#39;caaat&#39; t = &#39;cat&#39;<br>     01234       012<br>i tracks position in s <br>j tracks position in t <br><br><strong>Define the recurrence relation </strong><br>At each position i in s and j in t we have 2 choices <br>Choice 1 : characters match s[i] == t[i] <br>    - use s[i] to match t[j] - move both forward <br>      - count (i+1, j +1)<br>    - skip s[i] - move only i forward <br>      - count (i + 1, j)<br>    count(i,j) = count (i +1, j +1) + count (i +1, j )<br><br>Choice 2 : characters dont match s[i] != t[j]<br>      must skip s[i] - move only i forward <br>      count(i,j) = count (i +1, j)<br><br>s = &#39;caaat&#39; t = &#39;cat&#39;<br>at i = 1 (a) and j = 1 (a) - they match <br>but we have 3 a&#39;s in s - each one can be used to match t[1]<br>So we need to count all possibilities <br>  - use first a : &#39;c[a]at&#39; matches &#39;cat&#39; <br>  - use second a : &#39;ca[a]t&#39; matches &#39;cat&#39;<br>  - use third a : &#39;caa[a]t&#39; wait, no &#39;t&#39; after<br>so we count each valid path<br>    <br><br><strong>Basecases</strong><br>if j == len(t):<br>      return 1 <br>    if i == len(s):  <br>      return 0<br><br>We will use the memoization approach <br><br><br>Time : O (n)<br>Space :O (n)<br></pre><pre>def distinct_subsequences(s,t):<br>  memo = {}<br>  def count (i,j):<br>    # base cases<br>    if j == len(t):<br>      return 1 <br>    if i == len(s):  <br>      return 0 <br><br>    # memo check <br>    if (i,j) in memo:<br>      return memo[(i,j)]<br><br>    # recurrence <br>    if s[i] == s[j]:<br>      memo[(i,j)] = count (i+1, j+1 + count (i +1, j)<br>    else:<br>      memo[(i,j)] = count (i +1 , j ) <br>      return memo[(i,j)]<br><br>  return count (0,0)</pre><h4>Coin Change</h4><pre>You are given an integer array coins rep coins of different<br>denominations (1 dollar, 5 dollar, etc) and an integer amount<br>that represents a target amount of money. <br><br>Return the minimum number of coins to make up the target and if <br>impossible return -1 <br><br>Assume you have an unlimited number of coins. <br><br>Example : coins = [1,5,10] , amount = 12 <br>output : 3 <br><br>For this you have 2 choices for each until we get the min<br>number of coins we can use up until we get the target. <br><br><strong>Assumptions </strong><br>- What if amount is 0 ?<br>- What if coins array is empty <br>- What if amount is impossible to make? <br>- Can we reuse coins? yes <br>- What if we have a negative amount?<br><br><strong>Define the state</strong><br>Find the minimum number of coins that can make up the exact target.<br><br><strong>Define the recurrence</strong> <br>At each amount i we try every coin. <br>Change = 1 + min(change(remaining-coin) - for every coin<br>1 - count current coin and take the cheapest path <br>Only try coin if remaining - coin &gt;= 0 <br><br><strong>Base cases</strong><br>if remaining == 0 : return 0 - exact amount made <br>if remaining &lt; 0 : return 1e9 - invalid path <br><br>We will use memoization <br><br>Time : O(n) * m - amount and number of coins <br>Space : O (n) - memo table and call stack <br></pre><pre>def coin_change(coins, amount):<br>    memo = {}<br>    def change(remaining):<br>      # base cases <br>      if remaining == 0: # exact amount <br>          return 0 <br><br>      # memo check <br>      if remaining in memo:<br>          return memo[remaining]<br>      fewest = 1e9 <br>      for coin in coins:<br>        if remaining - coin &gt;= 0:<br>          fewest = min(fewest, 1 + change(remaining- coin))<br><br>      memo[remaining] = fewest <br>        return memo[remaining]<br><br>    minimum_coins = change(amount)<br>    return -1 of minimum_coins &gt;= 1e9 else minimum_coins <br></pre><h4>Coin change 2</h4><pre>Its the same coins and amount but now we need to return the <br>number of combinations that make up the amount. <br>Order doesnt not matter. <br>coins = [1,2,5]<br>amount = 5 <br>output = 4 <br>combinations <br>5 <br>2+2+1 <br>2+1+1+1<br>1+1+1+1+1<br><br>coins = [1,2,5]<br>         0,1,2 <br>At each step i we have 2 choices <br>We can either use the coin again - change(i, remaining - coin[i])<br>skip to the next coin - change(i + 1, remaining)<br><br><strong>Define the states</strong><br>We need to find the number of combinations using coins from <br>index i onwards to make up remaining amount. <br><br><strong>Define the recurrence</strong> <br>change(i, remaining) = change(i, remaining-coins[i]) + change (i+1, remaining)<br><br><strong>Base cases</strong> <br>if remaining == 0 : return 1 - exact amount made <br>if remaining &lt; 0: return 0 - overshot <br>if i == len(coins): return 0 - no more coins to try <br><br>We will implement with memoization <br><br></pre><pre>def coin_change_2(coins, amount):<br>    memo = {}<br>    def change(i, remaining):<br>      # base cases<br>      if remaining == 0:<br>        return 1 <br>      if remaining &lt;0: <br>         return 0 <br>      if i == len(coins): <br>         return 0 <br>      # Memo check<br>      if (i,remaining) in memo:  <br>         return memo[(i,remaining)] <br>      # Compute the memo <br>      memo(i, remaining) = change(i, remaining-coins[i]) + change (i+1, remaining)<br>        return memo[(i,remaining)]<br><br>    return change(0, amount)</pre><h4><strong>House Robber</strong></h4><pre>You are given an interger array nums where nums[i] rep the amount of <br>money the ith house has. <br><br>So for this you have 2 choices. You can either rob the house or not <br>rob it. And you cannot rob houses next to each other since they will<br>alert the police hence you have to skip the house next to each other.<br><br>You need to return the maximum amount of money you can rob without<br>alerting the police. <br><br>Example:<br>nums = [1,1,3,3]<br>Output = 4 <br><br>You can either pick num[0] and num[2] or num[1] and num[3] since<br>they are similar <br><br>Assumptions<br>- what if array is empty<br>- What happens if the array has only 1 house <br>- What happens when it has 2 houses <br>- Think of negative values <br><br><strong>Define the states</strong><br>- We need to find the maximum amount money you can rob from houses that<br>are not adjacent to each other without alerting the police. <br><br><strong>Define the recurrence relation</strong><br>At each house we have 2 choices <br>- rob it : take nums[i] and skip next house <br>- dont rob it : skip it and move to the next house <br>dfs(i) = max(nums[i] + dfs(i+2), dfs(i + 1))<br><br>Basecases <br>if i &gt;= len(nums):<br>   return 0  # we are out of bound <br><br>Will use the memoization approach <br><br></pre><pre>def house_robber(nums):<br>  memo = {}<br>  def rob(house):<br>    # Base case: No more houses to rob<br>    if house &gt;= len(nums):<br>        return 0<br><br>    if house in memo:<br>        return memo[house]<br><br>    memo[house] = max(nums[house] + rob(house + 2), rob(house + 1))<br><br>    return memo[house]<br><br>  return rob(0)<br>  </pre><h4>House Robber 2</h4><pre>Similar to house robber 1 but now the houses are arranged in a circle <br>meaning the first and last house are now adjacent to each other. <br><br>nums = [1,2,3,1]<br>output = 4 (rob house 0 and house 2 )<br><br>Circle means house 0 and house n -1 are neighbors <br>How can we handle this? <br>- case 1: rob house 0 to n-2 - exclude the last house <br>- case 2 : rob house 1 to n-1 - exclude first house <br><br>ans = max(case1, case2)<br><br><strong>Define the states</strong> <br>- Maximum money from house i up to end index <br><br><strong>Define the recurrence relation</strong><br>same as house robber 1:<br>dfs(i) = max(nums[i] + dfs(i+2), dfs(i+1))<br><br>just with a different end point for each case<br><br><strong>Base case</strong> <br>if i &gt;=end:<br>    return 0 </pre><pre>def house_robber_two(nums):<br>    memo = {}<br>    n = len(nums)<br><br>    if len(nums) == 1:<br>        return nums[0]<br><br>    def rob(house, last):<br>        # Base case: No more houses to rob<br>        if house &gt;= last:<br>            return 0<br><br>        if (house, last) in memo:<br>            return memo[(house, last)]<br><br>        memo[(house, last)] = max(<br>            nums[house] + rob(house + 2, last), rob(house + 1, last)<br>        )<br><br>        return memo[(house, last)]<br><br>    return max(rob(0, n - 1), rob(1, n))<br><br>&quot;&quot;&quot;<br>        Intuition:<br>        - Two choices:<br>            - Rob a house<br>            - Or not rob a house<br>        - If you decide to rob a house you must skip the adjacent house.<br>        - However robber must ensure maximum amount<br>        - Recurrence: rob(i) + rob(i + 2)<br>        - Maximum of current house robbed<br>        - If out of boundes: -&gt; 0<br><br>        Input: nums = [2,9,8,3,6]<br>                       0,1,2,3,4<br><br>        Output: 15<br><br>        memo = {<br>        (0, 3): 8,<br>        (2, 3): 8,<br>        (0, 4): 11<br>        (1, 4): 12<br>        (2, 4): 9<br>        (3, 4): 3<br><br>        }<br>        n = 5<br><br>        - rob(0, 3) -&gt; 8:<br>            - 2 + rob(3, 3), 0 -&gt; 2<br>                -<br>            - rob(2, 3) -&gt; 8:<br>                - 8 + rob(4, 3), 0: -&gt; 8<br>                    - rob(4, 3) -&gt; 0<br>                - rob(3, 3) -&gt; 0<br><br>        - rob(0, 4): -&gt; 12<br>            - 2 + rob(2, 4): -&gt; 11<br>                - rob(2, 4): -&gt; 9<br><br>            - rob(1, 4): -&gt; 12<br>                - 9 + rob(3, 4): -&gt; 3<br>                    - rob(3, 4): -&gt; 3<br>                        - 3 + rob(5, 4), 0: -&gt; 3<br>                        - rob(4, 4) -&gt; 0<br><br>                - rob(2, 4) -&gt; 9:<br>                    - 9 + rob(4, 4), 0: -&gt; 9<br>                    - rob(3, 4): -&gt; 3<br>        &quot;&quot;&quot;</pre><h4>Longest Palindromic Substring</h4><pre>Given a string s , return the longest substring of s that is <br>a palindrome. <br><br>s= &#39;babad&#39;<br>output = &#39;bab&#39; or &#39;aba&#39; both valid <br><br><strong>Assumptions </strong><br>- What happens if the string is empty <br>- Do we care if its case sensitive <br>- What is we have multiple longest palindromes of same length<br>- What happens if the string has only 1 character - palindrome<br><br>Every palindrome has a center <br>odd length : single character center &#39;bab&#39; - a <br>even length : 2 characters center &#39;bb&#39; - center in bb <br>So we try every possible center and then expand outwards while <br>characters match <br><br><strong>Define the states</strong><br>Palindrome (left, right) = is s[left:right+1] a palindrome <br>s = &#39;babad&#39;<br>     01234<br>expand from each center outwards <br><br><strong>Define the recurrence</strong> <br>expand(left, right):<br>    while left &gt;= 0 and right &lt; len(s) and s[left] == s[right]:<br>      expand outward<br>        left -= 1 <br>        right += 1 <br><br><strong>Basecases</strong> <br><br>if left &lt; 0 : stop <br>if right &gt;= len(s): stop <br>if s[left] != s[right] : stop - characters dont match <br><br>Time : O(n)<br>Space : O(n)</pre><pre>def longest_palindromic_substring(input_string):<br>    result = &quot;&quot;<br>    def expand(left, right):<br>        while left &gt;= 0 and right &lt; len(input_string) and input_string[left] == input_string[right]:<br>            left  -= 1<br>            right += 1<br>        return input_string[left+1:right]<br>    <br>    for i in range(len(input_string)):<br>        odd  = expand(i, i)      # odd length palindrome<br>        even = expand(i, i+1)    # even length palindrome<br>        <br>        if len(odd) &gt; len(result):<br>            result = odd<br>        if len(even) &gt; len(result):<br>            result = even<br>    <br>    return result</pre><h4>Palindromic Substring</h4><pre>Given a string s , return the number of substrings within s that <br>are palindromes. <br><br>s= &#39;abc&#39;<br>output = 3 <br>a, b, c<br><br>s= &#39;aaa&#39;<br>output = 6 <br>a , a ,a , aa, aa, aaa<br><br>Longest palindromic substring - return the longest palindrome<br>Palindromic substrings - count all palindromes <br><br><strong>Assumptions</strong> <br>- What if string is empty - return 0 <br>- Single characters count as palindromes - yes <br>- Case sensitive ? lower and upper case <br>- What if all characters are the same ? <br><br><strong>Define the states </strong><br>Count = total number of palindromic substrings <br>expand (left, right) = count all palindromes (expanding from center- left, right)<br><br><strong>Define the recurrence relation</strong><br>expand (left, right):<br>  while characters match and in bounds:<br>    count += 1 <br>    expand outward <br><br><strong>Basecases</strong> <br>if left &lt; 0: stop <br>if right &gt;= len(s): stop <br>if s[left] != s[right] : stop # not a palindrome<br><br>Time: O (n ^2)<br>Space : O (1)</pre><pre>def palindromic_substrings(input_string):<br>  count = 0 <br>  def expand (left, right):<br>    nonlocal count <br>    while left &gt;= 0 and right &lt; len(input_string) and input_string[left] == input_string[right]:<br>      count += 1 <br>      left -= 1 <br>      right += 1 <br>    for i in range (len(input_string)):<br>      expand(i, i)<br>      expand (i,i +1)<br><br>    return count </pre><h4>Maximum subarray</h4><pre>Given an array of integers nums, find the subarray with the largest <br>sum and return the sum. <br>nums = [2,-3,4,-2,-2,1,-1,4]<br>output = 8 <br><br><strong>Assumptions</strong><br>- What if array is empty? return 0 <br>- What if all numbers are -ve ? return the least -ve<br>- What if array has one element ? return that element<br>- Can the subarray be empty - yes <br><br>2 choices :<br>- extend previous subarray <br>- start a fresh <br><br>current_sum = maximum subarray sum ending at current index<br>max_sum     = maximum subarray sum seen so far<br><br>current_sum = max(nums[i], current_cum + nums[i])<br>max_sum = max(max_sum, current_sum)<br><br><br>We will use Kadane&#39;s algorithm. <br><br><br>Time : O(n) - pass through the array<br>Space : O(1) </pre><pre>def maxSubarray (nums):<br>  current_sum = max_sum = nums[0]<br>  for num in nums[1:]:<br>    current_sum = max(num, current_sum + num)<br>    max_sum = max(max_sum , current_sum)<br>  return max_sum </pre><h4>Maximum product subarray</h4><pre>Given an integer array nums, find a subarray that has the <br>largest product within the array and return it. <br><br>nums = [1,2,-3,4]<br>output = 4 </pre><pre>def max_product_subarray(nums):<br>  max_curr = nums[0]<br>  min_curr = nums[0]<br>  result = nums[0]<br><br>  for num in nums[1:]:<br>    candidates = (num, max_curr * num, min_curr * num)<br>    max_curr = max(candidates)<br>    min_curr = min(candidates)<br>       res = max(res, max_curr)<br>  return result </pre><p>I will do a 2nd article to do the remaining problems.</p><p>Happy coding.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1dfd7c00b1f4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[SECURITY, LOAD BALANCING AND OPTIMIZATION]]></title>
            <link>https://medium.com/@tales-with-ck/security-load-balancing-and-optimization-8a9120d6fdbe?source=rss-6a4c7355f365------2</link>
            <guid isPermaLink="false">https://medium.com/p/8a9120d6fdbe</guid>
            <category><![CDATA[optimization]]></category>
            <category><![CDATA[networking]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[load-balancing]]></category>
            <dc:creator><![CDATA[tales-with-ck]]></dc:creator>
            <pubDate>Mon, 13 Apr 2026 13:44:13 GMT</pubDate>
            <atom:updated>2026-04-13T13:44:13.211Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*G_h1i00RPN-L__DOCMD8tw.png" /></figure><h4><strong>Topics</strong></h4><ul><li>Security and access control</li><li>Firewalls</li><li>Proxies</li><li>VPNs (Virtual Private Network)</li><li>Load balancers</li><li>Network optimization</li></ul><p>So the whole concept is we want to control who gets in (security) and make sure once they’re in, things move fast (load balancing/optimization).</p><h4>Security and access control</h4><h4><strong>1. ACL (Access Control List)</strong></h4><p>They are used to filter traffic in a network. (The “bouncer” at the door).</p><p><strong>Types of Access Control List</strong></p><ul><li>Standard : Filters based ONLY on the source IP address.</li><li>Extended : Filters based on source/destination IP , protocol and port numbers.</li></ul><p>ACLs are stateless (they look at each packet alone) hence firewalls evolved to fix this.</p><h4><strong>2. FIREWALLS</strong></h4><p>Monitor and controls traffic based on rules.</p><p>It established a barrier between a trusted internet network and an untrusted external network.</p><p>The main functions :-</p><ul><li>Packet filtering</li><li>Stateful inspection</li><li>Proxying</li><li>Network Address Translation (NAT)</li></ul><p>In firewalls we have zones for them.</p><ul><li><strong>DMZ (Demilitarized Zone):</strong> Semi-isolated segment between internal and external networks. (Hosts public servers like web/mail).</li><li><strong>Internal Zone:</strong> The trusted, secure core of the network.</li><li><strong>External Zone:</strong> The untrusted portion (the public internet).</li></ul><p><strong>Types of firewalls</strong></p><ul><li><strong>Packet Filtering Firewalls (Stateless):</strong></li></ul><p><em>How:</em> Examines packets individually against rules (IP, port, protocol).</p><p><em>Characteristics:</em> Simple, fast, but vulnerable to IP spoofing (because it doesn’t remember previous packets).</p><p><em>Rule Example:</em> Allow TCP traffic from <em>Any</em> source to Destination 192.168.1.100 on Port 80 (HTTP).</p><ul><li><strong>Stateful Inspection Firewalls:</strong></li></ul><p><em>How:</em> Keeps track of active connections (a “state table”).</p><p><em>Characteristics:</em> Context-aware. If you request a website, it allows the <em>response</em> back in automatically. Better security than stateless.</p><ul><li><strong>Proxy Firewalls (Application-Level Gateway):</strong></li></ul><p><em>How:</em> Acts as an intermediary (middleman). The client talks to the proxy, the proxy talks to the internet.<br><em>Characteristics:<br></em>Content inspection (looks inside the data).<br><strong>Forward Proxy:</strong> Represents <em>clients</em> (hides internal users).<br><strong>Reverse Proxy:</strong> Represents <em>servers</em> (hides backend servers (often a Load Balancer)</p><ul><li><strong>Next-Generation Firewalls (NGFW):</strong></li></ul><p><em>How:</em> Traditional firewall + advanced features.<br><em>Characteristics:<br></em><strong>Deep Packet Inspection (DPI):</strong> Looks at the <em>payload</em> of the packet (not just the header).<br><strong>Intrusion Prevention Systems (IPS):</strong> Actively blocks malicious activity.<br><strong>Application Awareness:</strong> Can identify “Facebook” traffic regardless of port.</p><h4><strong>3. PROXIES</strong></h4><p>An intermediary server acting as a gateway between a client and a server.</p><p>Why use a proxy? Privacy, Security, Content Filtering, Caching, Load Balancing, Access Control, Anonymity.</p><p><strong>Types of proxies</strong></p><ul><li><strong>Forward Proxy (Client-side):</strong> Sits <em>in front of clients</em>. Used to bypass geo-restrictions or filter employee traffic.</li><li><strong>Reverse Proxy (Server-side):</strong> Sits <em>in front of servers</em>. Used for load balancing, caching, and SSL termination.</li><li><strong>Transparent Proxy:</strong> Intercepts traffic without the user knowing (caching/content filtering).</li><li><strong>Explicit Proxy (HTTP/HTTPS/SOCKS):</strong> Client must configure to use it. SOCKS works at a lower level (TCP/UDP) and is more versatile.</li></ul><h4><strong>4. VPNs(Virtual Private Network)</strong></h4><p>Creates a secure, encrypted tunnel over a public network between a device and a VPN server. Masks your IP and encrypts data.</p><p>It takes your original packet (with your real IP), encrypts it, and wraps it inside a <em>new</em> packet with the VPN server’s IP.</p><p><strong>Key protocols</strong></p><ul><li><strong>PPTP:</strong> Old, insecure. (Don’t use).</li><li><strong>L2TP/IPsec:</strong> More secure, but often slow.</li><li><strong>IKEv2/IPsec:</strong> Good for mobile devices (handles network changes well).</li><li><strong>OpenVPN:</strong> Open source, very secure, gold standard.</li><li><strong>WireGuard:</strong> Newer, lighter, faster than OpenVPN.</li></ul><p>Simple cryptography concepts:-</p><ul><li><strong>Symmetric Encryption:</strong> Same key to encrypt and decrypt (Fast, used for the actual data).</li><li><strong>Asymmetric Encryption:</strong> Public/Private key pair (Slow, used to securely exchange the symmetric key).</li></ul><p><strong>VPN Modes</strong></p><ul><li><strong>Transport Mode:</strong> Encrypts only the payload (used between servers).</li><li><strong>Tunnel Mode:</strong> Encrypts the entire packet (used for VPNs).</li></ul><p><strong>IPsec Components:</strong></p><ul><li><strong>AH (Authentication Header):</strong> Integrity &amp; authentication (no encryption).</li><li><strong>ESP (Encapsulating Security Payload):</strong> Encryption, integrity, authentication (The workhorse).</li><li><strong>IKE (Internet Key Exchange):</strong> Manages keys and negotiates <strong>Security Associations (SAs)</strong>.</li><li><strong>Diffie-Hellman (DH):</strong> The algorithm to securely exchange keys over a public network. (DH Group = strength of the key).</li></ul><p>Insight :</p><ul><li><strong>IPsec:</strong> Network-layer (Layer 3). Requires client software. Better for full network access (Site-to-Site).</li><li><strong>SSL VPN:</strong> Application-layer (Layer 7). Often uses a web browser. Better for client-to-site (remote employee accessing email).</li></ul><h4>5. LOAD BALANCER</h4><p>It distributes incoming network traffic across multiple servers.</p><p><strong>Features</strong></p><ul><li>Health Checks (stops sending traffic to dead servers).</li><li>Session Persistence (Sticky sessions: keeps a user on the same server).</li><li>SSL Termination (decrypts HTTPS once, sends HTTP to backend servers).</li><li>Content-based Routing (send /video to server A, /images to server B).</li></ul><p>Algorithms</p><ul><li><strong>Round Robin:</strong> Rotates through the list (Server 1, 2, 3, 1). <em>Best when servers are identical.</em></li><li><strong>Least Connections:</strong> Sends to the server with the fewest active connections. <em>Best for long-lived connections (streaming, databases).</em></li><li><strong>IP Hash (or Consistent Hashing):</strong> Uses client IP to pick a server. <em>Ensures the same client always hits the same server (good for caching).</em></li><li><strong>Weighted Round Robin / Least Connections:</strong> Same as above, but you assign “weights” (Server A is 3x faster than Server B, so it gets 3x the traffic).</li></ul><h4>NETWORK OPTIMIZATION</h4><p>It is created on 4 pillars.</p><ul><li>Speed (low latency, high throughput)</li><li>Reliability (no downtime)</li><li>Capacity (handles growth)</li><li>Security (protected while fast)</li></ul><p><strong>Metrics</strong></p><p><strong>Speed:</strong> Bandwidth (max), Latency (time), Throughput (actual), Packet loss.</p><p><strong>Reliability:</strong> Uptime (%), MTBF (time between failures), MTTR (time to fix).</p><p><strong>Capacity:</strong> Utilization (%), Connection count, Growth trends.</p><h4>1. SPEED</h4><p>Its how fast data moves across the network.</p><p>Speed metrics are : — <strong>bandwidth utilization, latency , throughput and packet loss.</strong></p><p><strong>Bandwidth</strong> — maximum data transfer rate of a network or internet connection measured in bits per second. It indicates the amount of data that can be transmitted over a network connection in a given amount of time.</p><p>The higher the bandwidth the more the data can be sent or received at once.</p><p>Bandwidth utilization is when we measure the percentage of the total available bandwidth.</p><p><strong>Latency</strong> is the time it takes for data to travel from the source to the destination. Hence lower latency indicates better network performance.</p><p><strong>Throughput</strong> it is the actual amount of data successfully transmitted over a network in a given period (bits per second). Video streaming needs high throughput.</p><p>To optimize throughput you can use load balancing, increase bandwidth and minimize network congestion.</p><p><strong>Packet loss</strong> is the percentage of packets that are lost during transmission.</p><p>Lower packet loss rates indicates a more reliable and faster network while high packet loss can severely degrade the network performance.</p><p>You need to <strong>upgrade hardware (</strong>routers, switches, NICs<strong>), optimize network protocols (</strong>TCP tuning, QUIC) , <strong>minimize latency (</strong>CDNs and Edge computing<strong>) and minimize packet loss (</strong>congestion control and traffic shaping).</p><blockquote>Low latency + low packet loss = real speed</blockquote><h4><strong>2. RELIABILITY</strong></h4><p>This is about keeping the network working consistently over time.</p><p>We need to add redundancy, load balancing and regular maintenance.</p><p>Key metrics</p><p><strong>Uptime (%)</strong></p><ul><li>Example: 99.99% uptime (“four nines”)</li><li>Measures availability</li></ul><p><strong>MTBF (Mean Time Between Failures)</strong></p><ul><li>Average time system runs before failure</li></ul><p><strong>MTTR (Mean Time To Repair)</strong></p><ul><li>How quickly issues are fixed</li></ul><p>How to improve reliability</p><ul><li>Redundancy : having multiple servers, links and data centers.</li><li>Failover systems : automatic switching when failure occurs</li><li>Load balancing : Distribute traffic across systems.</li><li>Monitoring and altering : detect failures early</li></ul><p>Example platforms such as Netflix rely heavily on redundancy across regions</p><h4>3. CAPACITY (SCALABILITY)</h4><p>This is about handling growth without degrading performance.</p><p>Capacity metrics</p><ul><li>Network utilization, connection count , data volume and growth trends</li></ul><p>This is a question you need to ask yourself can your network handle 10x more users tomorrow or in a few years?</p><p><strong>Utilization (%)</strong></p><ul><li>How much of resources are being used</li><li>High utilization → risk of congestion</li></ul><p><strong>Connection count</strong></p><ul><li>Number of active users/connections</li></ul><p><strong>Growth trends</strong></p><ul><li>Traffic increase over time</li></ul><p>How to improve capacity</p><ul><li>Horizontal scaling : ading more servers instead of upgrading one</li><li>Auto-scaling systems</li><li>Efficient resource allocation</li><li>Traffic distribution</li></ul><h4>4. SECURITY</h4><p>Think of this as protecting the network while maintaining performance.</p><p>Key considerations</p><ul><li>Encryption (TLS)</li><li>Firewalls</li><li>Intrusion dection systems (IDS/IPS)</li><li>DDoS protection</li></ul><p>Example : HTTPS adds encryption overhead, but modern protocols (like HTTP/3) reduce the performance impact.</p><p>REAL WORLD SYSTEM EXAMPLE: Youtube</p><ul><li>Speed : smooth video playback</li><li>Reliability : no buffering / crashes</li><li>Capacity : millions of user simultaneously</li><li>Security : protected stream and user data</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8a9120d6fdbe" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[LAYER 7 — APPLICATION LAYER]]></title>
            <link>https://medium.com/@tales-with-ck/layer-7-application-layer-3221bb73df76?source=rss-6a4c7355f365------2</link>
            <guid isPermaLink="false">https://medium.com/p/3221bb73df76</guid>
            <category><![CDATA[faang-interview-prep]]></category>
            <category><![CDATA[network]]></category>
            <category><![CDATA[osi-model]]></category>
            <dc:creator><![CDATA[tales-with-ck]]></dc:creator>
            <pubDate>Sat, 11 Apr 2026 11:09:43 GMT</pubDate>
            <atom:updated>2026-04-11T11:09:43.129Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vR0azp1CM1UtgTEftJ3NBA.png" /><figcaption>Application layer</figcaption></figure><ul><li>HTTP (Hypertext Transfer Protocol)</li><li>HTTP methods</li><li>HTTP status codes</li><li>HTTP/2 vs HTTP/3</li><li>Differences between HTTP/2 AND HTTP/3</li><li>DNS</li><li>DHCP</li><li>Remote Shell Access — Telnet , SSH</li><li>Interview scenarios</li></ul><p>It is responsible for providing network services and protocols that enable applications to communicate with each other over a network.</p><h4>HTTP (Hypertext Transfer Protocol)</h4><p>Its a stateless protocol , meaning that each request-reponse cycle is independent and doesn&#39;t retain any information from previous interactions.</p><p>It is used for transmitting and receiving web pages and other resources on the WWW.</p><p>The browser initiates a HTTP request, the server processes the request and sends back a HTTP response to the browser.</p><p>A client sends a HTTP request to a server, the server responds with a HTTP response.</p><p>The request consists of: a method (<strong>GET, POST, PUT, DELETE</strong>), a target resource (<strong>URL or URI</strong>), headers and an optionally a message body.</p><p>The response includes: a status code, headers and an optional response body.</p><h4>HTTP Methods</h4><ul><li><strong>GET</strong> : Retrieve a resource</li><li><strong>POST</strong> : Submit data to be processed by the server</li><li><strong>PUT</strong> : Store or update a resource</li><li><strong>DELETE </strong>: Remove a resource</li><li><strong>HEAD</strong> : Retrieve only the headers of a resource</li><li><strong>OPTIONS</strong> : Retrieve the supported methods and capabilities of a server</li><li><strong>PATCH</strong> : Partially update a resource</li></ul><p>To maintain state or session information, mechanisms like cookies or session tokens are commonly used.</p><p><strong>HTTP cookies — </strong>are small pieces of data stored on the client-side by websites to track user activity and store user-specific information. They are stored as key-value pairs in the user’s browser.</p><p><strong>Cookie attributes</strong></p><ul><li><strong>Name and value</strong> : The key value pair that stores the data.</li><li><strong>Domain</strong> : specifies the domain(s) to which the cookie applies.</li><li><strong>Path</strong> : Defines the URL path(s) within the domain for which the cookie is valid.</li><li><strong>Expiration</strong> : Specifies when the cookie should expire.</li><li><strong>Secure and HTTP only</strong> : Flags that control whether the cookie should be sent only over HTTPS and if it can be accessed by client-side scripts respectively.</li></ul><p><strong>HTTP Headers</strong></p><p>HTTP requests and responses include headers that provide additional information and instructions.</p><p>Common headers include:-</p><ul><li>Content type (specifies the MIME type of the data)</li><li>Content length (Indicates the size of the message body)</li><li>User agent (provides information about the client)</li><li>Server (Identifies the server software)</li></ul><h4><strong>HTTP Status Codes</strong></h4><p>It includes status codes that indicate the outcome of the request.</p><p>Common status code range: —</p><ul><li><strong>1xx</strong> : Informational</li><li><strong>2xx</strong> : Success</li><li><strong>3xx</strong> : Redirection</li><li><strong>4xx</strong> : Client errors</li><li><strong>5xx</strong> : Server errors</li></ul><h4>HTTP/2 and HTTP/3</h4><p>Newer version of the HTTP protocol designed to improve performance and efficiency.</p><p><strong>HTTP/3</strong> is based on QUIC protocol which is built on top of UDP (User Datagram Protocol).</p><p><strong>HTTP/2</strong> introduces multiplexing which allows multiple streams of data to be transferred simultaneously over a single TCP connection.</p><p>A limitation that might occur in TCP is <strong>head of line blocking</strong>. This means when a single packet is lost during transmission. TCP has to wait for that specific packet to be retransmitted before the rest of the steam can proceed which causes delays for all stream sharing that connection.</p><p><strong>HTTP/3</strong> leverages QUIC hence it eliminates the issue of head-of-line blocking since it uses UDP and introduces multiplexed streams directly at the protocol level. If a stream experiences a packet loss the other streams are unaffected and can continue transmitting data without delays.</p><p><strong>Head of line blocking</strong> its a performance bottleneck that occurs when a packet at the front of the queue prevents other packets behind it from being processed even though these packets might be ready to move forward.</p><p><strong>HTTP/2</strong> head of line blocking happens at 2 levels :- TCP level and HPACK header compression. (If header frames are lost or delayed , the dependency between header for different streams can cause the entire connection to stall).</p><h4>Differences between HTTP/2 and HTTP/3</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*STihqm0otVChXg6a-l4XIQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EbLl7prVgpmzH_HIWcSDLg.png" /></figure><h4>DNS (Domain Name System)</h4><p>It translates the domain name to an IP address.</p><p>The client first checks its local cache to see if the IP address for the domain is already stored.</p><p>If not found, the client sends a <strong>DNS query</strong> to a <strong>DNS resolver</strong>.</p><p>The resolver recursively traverses the DNS hierarchy, starting from the root servers, then moves to the TLD servers and finally to the authoritative servers until it obtains the IP address associated with the domain.</p><p>The resolver caches the obtained IP address for future use and returns its to the client.</p><p>Various types of records:-</p><p>- <strong>A (Address)</strong> : Maps a domain name to an IPv4 address.</p><p>- <strong>AAAA (IPv6 Address)</strong> : Maps a domain name to an IPv6 address.</p><p>- <strong>CNAME (Canonical Name)</strong> : Provides an alias or canonical name for a domain.</p><p>- <strong>MX (Mail Exchanger)</strong> : Specifies the mail server responsible for accepting incoming emails.<br>- <strong>TXT (Text)</strong> : Stores arbitrary text info associated with a domain<br>- <strong>NS (Name Server)</strong> : Indicates the authoritative DNS servers for a domain</p><p>They implement caching to improve perfomance and reduce the load on the network.</p><p>Caching helps store recently accessed domain records and IP addresses allowing subsequent queries for the same domain to be resolved more quickly.</p><h4>DHCP (Dynamic Host Configuration Protocol)</h4><p>It automatically assigns IP addresses to devices on a network.</p><p>It simplifies the process of configuring IP addresses and network settings by providing a centralized mechanism for dynamic IP address allocation.</p><p>It allows devices to obtain IP addresses, subnet masks, default gateways, DNS servers and other configuration parameters automatically.</p><p>It operates in a client-server model.</p><p>DHCP clients request IP address assignment and network configuration parameters from DHCP servers.</p><p>The servers are responsible for managing and assigning IP addresses and network settings to DHCP clients.</p><p>When a client requests an IP address, the DHCP servers leases an IP address to the client for a specific lease time.</p><p><strong>DHCP Relay</strong></p><p>- Typically a router or switch, forwards DHCP messages between clients and servers.</p><p>- The relay agent listens for DHCP broadcasts from clients, encapsulates the messages and sends them to the DHCP server.</p><p>- The DHCP server responds through the relay agent which forwards the response to the client.</p><p><strong>DHCPv6 </strong>— it provides functionality to DHCP for IPv4 but uses different message formats and protocols for IPv6 address assignment and configuration.</p><h4>Remote Shell Access — Telnet , SSH</h4><ol><li><strong>Telnet</strong></li></ol><p>Its a network protocol used for remote access to servers and network devices.</p><p>It does not provide encryption or secure authentication mechanisms. Data transmitted over Telnet is sent in plaintext, making it vulnerable to interception and unauthorised access.</p><p>It typically <strong>uses port 23</strong> for communication.</p><p>Features:-</p><ul><li>Remote shell access: Telnet allows users to log in remotely to a server and execute commands.</li><li>Device configuration: Telnet is commonly used to remotely configure network devices, such as routers and switches.</li><li>Simplicity: Telnet is relatively straightforward and easy to use compared to SSH but lacks the security features provided by SSH.</li></ul><p>2. <strong>SSH (Secure shell)</strong></p><p>It is a secure network protocol that provides secure remote login, command execution, and secure file transfer between two computers.</p><p>Encrypts the data exchanged between the client and server, providing confidentiality and integrity of the communication.</p><p>It supports multiple authentication methods, including password-based authentication, public key authentication, and host-based authentication.</p><p>It typically <strong>uses</strong> <strong>port 22</strong> for communication.</p><p>Features</p><ul><li>Secure remote shell access: SSH allows users to log in remotely to a server and execute commands securely.</li><li>Secure file transfer: <strong>SSH</strong> includes <strong>SFTP</strong> (SSH File Transfer Protocol) and <strong>SCP</strong> (Secure Copy) for secure file transfer between client and server.</li><li>Port forwarding: SSH allows creating secure tunnels for forwarding network connections, enabling secure access to remote services.</li><li>X11 forwarding: SSH supports X11 forwarding, allowing the forwarding of graphical applications over the encrypted SSH connection.</li></ul><h4>Differences in telnet and ssh</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Jz2j5FJd9a7xQAu6Xs1L1A.png" /></figure><h4>Putting it all together</h4><p>Data is <strong>encapsulated</strong> from application layer then keeps adding headers as well as one moves down the layers.</p><p>The <strong>decapsulation</strong> happens by removing the headers as well as one goes down the layers.</p><p><strong>PING</strong> happens at the ICMP protocol. It servers as a messaging system for IP networks, facilitating error reporting, network diagnostics and control functions.</p><h4>Interview scenarios</h4><pre>What happens when you type meta.com into a browser and hit Enter?<br><br>Why wouldn&#39;t you want a root DNS server to answer queries for you, <br>instead of delegating you to an authoritative server?<br><br>Describe how you would identify and resolve an issue with a service <br>that is no longer responding to requests.<br><br>What options do you have, nefarious or otherwise, to stop people on <br>a wireless network you are also on (but have no admin rights to) from <br>hogging bandwidth by streaming videos?<br><br>How does DNS resolution work?</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3221bb73df76" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[LAYER 5 — SESSION LAYER]]></title>
            <link>https://medium.com/@tales-with-ck/layer-5-session-layer-07e68e8c7564?source=rss-6a4c7355f365------2</link>
            <guid isPermaLink="false">https://medium.com/p/07e68e8c7564</guid>
            <category><![CDATA[networking]]></category>
            <category><![CDATA[faang-interview-prep]]></category>
            <dc:creator><![CDATA[tales-with-ck]]></dc:creator>
            <pubDate>Sat, 04 Apr 2026 08:06:48 GMT</pubDate>
            <atom:updated>2026-04-04T08:06:48.441Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rASuiCDDMhWRlIXKBQgF8g.png" /></figure><h4>Differences of TLS1.2 vs TLS 1.3</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EWxlwUguymZE_Se5qNoWiA.png" /></figure><h4>TLS TERMINATION</h4><p>TLS termination is the process of decrypting encrypted TLS/SSL traffic at a specific point in the network, so it can be processed in plain text.</p><p>Where it happens: It’s most commonly performed on a load balancer or a reverse proxy</p><p><strong>How it works</strong></p><ul><li>Client connects: The client (e.g., a user’s browser) initiates a secure TLS handshake with the load balancer.</li><li>Load Balancer holds certificate: The load balancer presents its SSL/TLS certificate to the client and completes the handshake. The client sees the load balancer as the “server.”</li><li>Encrypted — Decrypted: All traffic between the client and the load balancer remains encrypted.</li><li>Termination Point: The load balancer decrypts the incoming traffic (this is the “termination” of the TLS connection).</li><li>Internal Forwarding: The load balancer then forwards the now unencrypted HTTP request to the backend application servers based on its load-balancing rules.</li><li>Return Path: The backend server sends a plain text response back to the load balancer, which re-encrypts it before sending it back to the client.</li></ul><p>Happy networking.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=07e68e8c7564" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Layer 4 — TRANSPORT LAYER]]></title>
            <link>https://medium.com/@tales-with-ck/layer-4-transport-layer-35b4d939b261?source=rss-6a4c7355f365------2</link>
            <guid isPermaLink="false">https://medium.com/p/35b4d939b261</guid>
            <category><![CDATA[networking]]></category>
            <category><![CDATA[osi-model]]></category>
            <category><![CDATA[layer-4]]></category>
            <category><![CDATA[faang]]></category>
            <dc:creator><![CDATA[tales-with-ck]]></dc:creator>
            <pubDate>Tue, 31 Mar 2026 08:16:03 GMT</pubDate>
            <atom:updated>2026-03-31T08:16:03.981Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8myY5ZuP9Yg23HQm69bY2Q.png" /></figure><p>The transport layer facilitates communication between applications running on different computers.</p><p>Topics</p><ul><li>TCP</li><li>UDP</li><li>QUIC</li><li>Port numbers</li><li>Maximum Segment Size (MSS)</li><li>MTU vs MSS</li><li>Interview scenarios</li></ul><p>It breaks data into segments, assigns and destination port numbers to identify applications and utilizes protocols such as TCP and UDP.</p><p>Port numbers are unique identifiers used by the transport layer to identify specific applications or services running on a host.</p><p>The posts range from 0 to 65535 and are divided into 3 ranges:-</p><ul><li><strong>Well known ports (0–1023)</strong> — ports assigned to specific services or applications universally recognized across different systems.</li><li><strong>Registered ports (1024–49151)</strong> — ports assigned by the Internet Assigned Numbers Authority (IANA) to specific services or applications upon request.</li><li><strong>Dynamic or private ports (49152–65535)</strong> — ports are available for temporary or private use.</li></ul><p>When data is sent from an application, the transport layer assigns a source port number to identify the application on the sender’s side. On the receiving end, the transport layer used the destination port number to deliver the received data to the appropriate application.</p><h3>TCP (Transmission Control Protocol)</h3><p>Its a reliable , connection-oriented transport layer protocol that ensures the ordered and error-free delivery of data packets over IP packets.</p><p>Characteristics</p><ul><li>Reliability</li><li>Connection-oriented</li><li>Ordered delivery</li><li>Flow control</li><li>Congestion control</li><li>Connection termination</li><li>Error detection and correction</li><li>TCP header has 20–60 bytes</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UBBpcd0EXQYYYw6XwLx_Jw.png" /><figcaption>TCP Header</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-O5BZ392Y9j9F-tnmzlmdQ.png" /><figcaption>TCP header descriptions</figcaption></figure><p>TCP provides reliable data transmission by using acknowledgments, retransmissions and error detection mechanism to ensure that data arrives at the destination accurately and in the correct order.</p><p>TCP guarantees the ordered delivery of data packets. Each packet is assigned a sequence number and receiver reassembles the packets in the correct order based on these sequence numbers.</p><h4>TCP FLOW CONTROL</h4><p>TCP flow control is a mechanism that allows the receiver to control the amount of data sent by the sender at one time.</p><p>It ensures that the receiver’s buffer does not overflow by advertising a “window” size, which represents the amount of data it can receive before sending an acknowledgment.</p><p>Its ensures that the receiver can handle the incoming data at a rate it can process, preventing the receiver from being overwhelmed by a faster sender.</p><p>How it works: —</p><ul><li>Window Size Advertisement: The receiver includes a window size in its ACK packets, indicating how much data it can receive.</li><li>Sender’s Adjustment: The sender uses this window size to determine how much data it can send before waiting for an acknowledgment.</li><li>Sliding Window Mechanism: The sender maintains a sliding window of data that it can send. As the receiver acknowledges data, the sender slides the window forward, allowing it to send more data.</li></ul><h4>CONGESTION CONTROL</h4><p>TCP congestion control prevents network congestion on routers</p><p>The sender adjusts the <strong>congestion window (cwnd)</strong> based on network capacity, using ACKs and packet loss as signals.</p><p>Its responsible for preventing network congestion, which occurs when network resources are overloaded with more data than they can handle.</p><p>Congestion can lead to packet loss, increased latency, and degraded performance.</p><p><strong>How it works</strong>:-</p><p>TCP congestion control algorithms dynamically adjust the sending rate of data based on the perceived network conditions. They aim to find an optimal sending rate that utilizes the available network capacity without causing congestion.</p><p>Congestion control algorithms monitor network congestion signals such as packet loss and delay and adjust the sending rate by reducing or increasing the number of packets sent.</p><p><strong>How TCP handles network congestion</strong></p><p><strong>1. Slow Start</strong> Begins with a small congestion window (e.g., 1–10 MSS). The congestion window increases by 1 MSS per ACK received, which causes it to double every Round Trip Time (exponential growth), rapidly probing network capacity. Slow Start ends when the congestion window reaches the slow start threshold (<strong>ssthresh</strong>), or congestion is detected.</p><p><strong>2. Congestion Avoidance</strong> After Slow Start, the congestion window increases linearly (by 1 MSS per Round Trip Time). This is a slower, more cautious growth phase. Together with the reductions that follow packet loss, this linear growth forms the <strong>Additive Increase</strong> part of AIMD (Additive Increase/Multiplicative Decrease).</p><p><strong>3. Congestion Detection &amp; Recovery</strong> Packet loss (signalled by either a Timeout or Triple Duplicate ACK ) indicates congestion. The slow start threshold (<strong>ssthresh</strong>) is halved in both cases (<strong>Multiplicative Decrease</strong>), but the congestion window reduction differs depending on the TCP variant and the type of loss event:</p><ul><li><strong>TCP Tahoe:</strong> Both Timeout and Triple Duplicate ACK reset the congestion window to 1 MSS and re-enter Slow Start.</li><li><strong>TCP Reno:</strong> A Timeout resets the congestion window to 1 MSS and re-enters Slow Start. A Triple Duplicate ACK triggers <strong>Fast Recovery</strong> instead the congestion window is set to the new <strong>ssthresh</strong> (roughly half its previous value) and Congestion Avoidance resumes immediately, without dropping back to 1 MSS. Reno treats Triple Duplicate ACK as a milder signal since ACKs are still flowing, meaning the network is not fully congested.</li></ul><p><strong>How TCP handles out-of-order packets</strong></p><p>TCP uses sequence numbers and the ACK mechanism to detect and reorder packets that arrive out of sequence.</p><p><strong>1. Sequence numbers</strong> Every byte of data is assigned a sequence number before transmission. This allows the receiver to identify each segment’s position in the original data stream and reorder them correctly, regardless of the order they arrive in.</p><p><strong>2. Out-of-order arrival</strong> When a packet arrives out of order, the receiver does not discard it. Instead, it is held in a buffer while the receiver waits for the missing segment(s) that should have arrived earlier.</p><p><strong>3. Receiver acknowledgement</strong> TCP uses cumulative ACKs. The receiver only acknowledges the last in-order byte it has received. If a gap exists (i.e a missing segment), the receiver sends a duplicate ACK for the last in-order byte, signalling to the sender that something is missing.</p><p><strong>4. Selective Acknowledgement (SACK)</strong> SACK is an optional TCP extension that improves on basic cumulative ACKs. Rather than only reporting the last in-order byte, the receiver tells the sender exactly which segments have been received including out-of-order ones. This allows the sender to identify precisely which segments are missing.</p><p><strong>5. Retransmission of missing packets</strong> Without SACK, the sender must retransmit everything from the missing segment onward. With SACK enabled, the sender retransmits only the specific missing segments, making recovery faster and more efficient.</p><p><strong>What is the purpose of tcpdump?<br></strong>Tcpdump is a powerful command-line packet analyser.</p><p>It allows you to capture, display, and analyse network traffic passing through a network interface in real-time.</p><p>It is commonly used for network troubleshooting, traffic monitoring, and security analysis.</p><p><strong>Common commands</strong></p><p>sudo tcpdump -i eth0 Captures all traffic on a specific interface, in this case eth0. The -i flag specifies which network interface to listen on.</p><p>sudo tcpdump -i any host 192.168.1.5 Captures all traffic to and from a specific IP address (192.168.1.5). Using any for the interface tells tcpdump to listen on all available interfaces simultaneously.</p><p>sudo tcpdump -i any port 80 Captures all traffic on port 80, which is the default port for unencrypted HTTP traffic. Useful for inspecting web requests and responses.</p><p>sudo tcpdump -i any -w capture.pcap Captures raw packets and writes them to a file (capture.pcap) rather than displaying them in the terminal. The .pcap file can be opened later in a tool like Wireshark for deeper visual analysis. The -w flag specifies the output file.</p><p><strong>Troubleshooting with tcpdump</strong></p><p>tcpdump operates at the raw packet level, making it useful for three common scenarios:</p><ul><li><strong>Verifying connectivity:</strong> Confirms whether your machine is actually sending packets (e.g. SYN packets) to a server, rather than the issue being local.</li><li><strong>Diagnosing TLS issues:</strong> You can observe the ClientHello and ServerHello packets of a TLS handshake. If the handshake stalls at that point, it typically indicates a certificate problem.</li><li><strong>Identifying unexpected traffic:</strong> Reveals any unusual or unrecognised traffic appearing on a given port.</li></ul><p><strong>CWR and ECE Flags for Congestion Control</strong></p><p>These two TCP flags work together as part of <strong>Explicit Congestion Notification (ECN)</strong>, allowing routers to signal congestion without dropping packets.</p><p><strong>ECE — Explicit Congestion Notification Echo</strong> When a router detects congestion, it sets the ECN flag in the IP header of passing packets. When the receiver’s TCP stack sees this, it notifies the sender by setting the ECE flag in its ACK, signalling that congestion is occurring in the network.</p><p><strong>CWR — Congestion Window Reduced</strong> When the sender receives a segment with the ECE flag set, it responds by reducing its congestion window (cwnd). It then sets the CWR flag in its next outgoing segment to inform the receiver that it has acknowledged the congestion signal and has already reduced its sending rate. This prevents the receiver from continuing to echo congestion notifications unnecessarily.</p><p><strong>TCP Connection Termination</strong></p><p>TCP closes connections gracefully using a <strong>4-way handshake</strong>, ensuring both sides finish transmitting before closing. An immediate termination is also possible using a RST packet.</p><p><strong>The 4-way handshake</strong></p><ol><li>The client decides to close and sends a <strong>FIN</strong> packet to the server. Client state: ESTABLISHED — FIN_WAIT_1.</li><li>The server receives the FIN and sends an <strong>ACK</strong>. The server can still send its own data at this point. Server state: ESTABLISHED — CLOSE_WAIT. Client state on receiving ACK: FIN_WAIT_1 — FIN_WAIT_2.</li><li>When the server is ready to close, it sends its own <strong>FIN</strong>. Server state: CLOSE_WAIT — LAST_ACK.</li><li>The client receives the server’s FIN and sends a final <strong>ACK</strong>. Client state: FIN_WAIT_2 — TIME_WAIT. Server state on receiving ACK: LAST_ACK — CLOSED.</li><li>The client waits in TIME_WAIT for a period of <strong>2 × MSL (Maximum Segment Lifetime)</strong> to ensure the final ACK was received and to allow any delayed duplicate packets to expire. Client state then moves to CLOSED.</li></ol><p><strong>Troubleshooting commands</strong></p><ul><li>netstat -an | grep TIME_WAIT — check for connections stuck in TIME_WAIT</li><li>netstat -an | grep CLOSE_WAIT — check for connections stuck in CLOSE_WAIT</li></ul><p><strong>TCP Retransmissions</strong></p><p>When a segment is lost or corrupted in transit, TCP detects this and retransmits the affected data without involving the application layer.</p><p><strong>1. Detecting loss</strong> : TCP uses ACK numbers to track received segments. If the sender does not receive an ACK within the <strong>Retransmission Timeout (RTO)</strong>, it assumes the segment was lost or corrupted and retransmits it.</p><p><strong>2. Retransmission Timeout (RTO)</strong> : The RTO is the duration the sender waits for an ACK before retransmitting. It is dynamically adjusted throughout the connection based on current network conditions, such as round trip time.</p><p><strong>3. Fast Retransmit</strong> : Rather than always waiting for the RTO to expire, TCP uses <strong>Fast Retransmit</strong>. If the sender receives <strong>three duplicate ACKs</strong> (three ACKs for the same sequence number), it infers that a segment has been lost and retransmits it immediately, without waiting for the timeout. This is faster and less disruptive than waiting for the RTO.</p><p><strong>4. Retransmission process</strong> : The sender resends the original segment with the same sequence number. The receiver acknowledges it on receipt, confirming successful delivery and allowing the connection to continue.</p><p><strong>5. Selective Retransmission (SACK)</strong> Rather than retransmitting everything from the point of loss, TCP uses <strong>Selective Acknowledgement (SACK)</strong> to retransmit only the specific missing segments. This avoids redundant retransmissions and makes more efficient use of network resources.</p><h3>UDP (User Datagram Protocol)</h3><p>Its a connectionless and unreliable transport layer protocol in the TCP/IP protocol suite.</p><p>It provides a minimalistic and lightweight mechanism for sending datagrams between devices on an IP network.</p><p>Characteristics</p><ul><li>Connectionless protocol</li><li>Unreliable delivery</li><li>8 bytes header</li><li>Stateless communication</li><li>Fast transmission</li><li>Broadcast and multicast support</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-7_-7_koYromGWUtUt0l8g.png" /><figcaption>UDP header</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-C_LZWY4GRCKYEgyi2UnwQ.png" /><figcaption>Fields description</figcaption></figure><h4>Differences in TCP and UDP</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IWGgPiUT5-0ifgK0743p5A.png" /><figcaption>Differences in TCP and UDP</figcaption></figure><h3><strong>QUIC (Quick UDP Internet Connections)</strong></h3><p>It was developed by Google and designed to overcome the limitations of the traditional transport protocols such as TCP and UDP.</p><p>It provides low latency, reliability and secure transport for web traffic mostly in real time applications and mobile networks.</p><p>Its built on top of UDP leveraging its lightweight and connectionless ways.</p><p><strong>Multiplexing in QUIC</strong></p><ul><li>Single Socket Communication: In QUIC, all communication, including multiple streams, occurs over a single socket. Unlike TCP, which requires separate connections for each stream, QUIC multiplexes multiple streams within a single connection.</li><li>Stream Identification: Each QUIC stream is uniquely identified by a Stream ID, allowing the client and server to distinguish between different streams within the same connection. Stream IDs are bidirectional, meaning that both the client and server can initiate streams.</li><li>Concurrent and Independent Streams: QUIC allows for concurrent transmission of multiple streams within a single connection. This means that multiple streams can progress simultaneously without being blocked by each other. Each stream operates independently, and the order of the packets within different streams does not affect each other, addressing the head-of-line blocking problem found in TCP.</li><li>Stream Prioritization: QUIC supports stream prioritization, enabling the client or server to assign priority levels to different streams. By assigning priorities, important or time-sensitive streams can be given higher priority, ensuring their delivery and reducing the impact of lower-priority streams on the overall performance.</li></ul><p><strong>Benefits of multiplexing</strong></p><ul><li>Increased Efficiency: Multiplexing allows for more efficient use of the network connection by transmitting multiple streams simultaneously. It reduces the need for establishing multiple connections and the associated overhead.</li><li>Reduced Latency: QUIC’s multiplexing capabilities help reduce latency by eliminating the need for multiple round trips to establish separate connections for each stream.</li><li>Enhanced Performance: Multiplexing enables better resource utilization and improves overall performance by allowing parallel processing of multiple streams without blocking.</li></ul><p><strong>QUIC INTEGRATION WITH HTTP/3</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nJTp7tIAesApW4TGk8VZQQ.png" /></figure><h4>Port numbers</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*D43Hq_1-6PtYHXmy_GrU5w.png" /><figcaption>Port Numbers</figcaption></figure><h4>MSS (Maximum Segment Size)</h4><p>MSS is the maximum application data payload in a TCP segment, excluding TCP and IP headers.</p><p>MSS = MTU — 40 bytes for standard IPv4/TCP.</p><p>While MTU is the maximum packet size a network link can carry, including all headers.</p><p>MSS is a concept at the transport layer (Layer 4) and is specific to TCP</p><p>How is MSS determined? During TCP 3-way handshake, each side advertises its MSS based on its local MTU. The connection uses the smaller of the two advertised values. For example, if client MSS=1460 and server MSS=1360, they’ll use MSS=1360.</p><p>MSS prevents IP fragmentation by ensuring TCP segments, when encapsulated with IP and TCP headers, don’t exceed the path MTU. This avoids the performance overhead of fragmentation and reassembly.</p><p>For example, in Ethernet, the standard MTU is 1500 bytes. The IP header is 20 bytes (without options) and the TCP header is 20 bytes (without options).</p><p>MSS = 1500–20–20 = 1460 bytes</p><p>What happens if MSS is set too high? Packets get fragmented by routers, leading to increased overhead, potential out-of-order delivery, and if any fragment is lost, the entire packet must be retransmitted. In worst cases, packets get silently dropped if fragmentation is needed but DF bit is set.</p><p>PMTUD is a technique where TCP sets the DF (Don’t Fragment) bit and gradually increases packet size. If a router can’t forward a packet without fragmentation, it sends an ICMP ‘Fragmentation Needed’ message, allowing TCP to discover the path’s minimum MTU.</p><p>Why might you manually set MSS? In scenarios like VPN tunnels, PPPoE connections, or when PMTUD fails due to firewalls blocking ICMP, manually setting a lower MSS can prevent connectivity issues and fragmentation problems</p><h4>MTU (Maximum Transmission unit)</h4><p>It limits the maximum payload size in a single UDP datagram in an underlying network.</p><p>If payload exceeds the MTU, fragmentation may occur.</p><h4><strong>Differences in MTU and MSS</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dbXg4CTrwoCt9HFhvLnWXA.png" /></figure><h4>Interview scenarios</h4><pre>1. Explain how TCP handshake works&quot; and &quot;What&#39;s the difference between TCP and <br>UDP?<br><br>2. TCP flow control and congestion control — covered in the networking <br>fundamentals round, alongside packet drop analysis and H-S-R-S-H <br>communication scenarios.<br><br>3. Describe how the TCP header in an IPv4 packet changes going from <br>device to device, and which fields change within the header<br><br>4. How would you send packets to remote machines and try to upgrade them <br>remotely? How would you troubleshoot if some machines are not updated?<br><br>5. Walk me through how you&#39;d troubleshoot a server that&#39;s not reachable<br><br>6. What&#39;s the maximum theoretical number of TCP connections a host may <br>have open?<br><br>7. You&#39;re on-call and monitoring a backend service at Meta. <br>A dashboard alert fires: the server has 80,000 sockets in <br>TIME_WAIT state. No new connections are being accepted. <br>Walk me through what&#39;s happening, why it matters, and how you&#39;d resolve it.<br><br>8. A Meta data pipeline is transferring large files between two datacenters. <br>Engineers report throughput has dropped from 10Gbps to ~200Mbps with no <br>changes deployed. When you run a packet capture, you see heavy TCP <br>retransmissions. How do you diagnose this and what Layer 4 mechanisms <br>are at play?<br><br>9. A Meta-facing API endpoint is completely unresponsive to new clients, <br>but existing connections are working fine. You check the server and find <br>the SYN backlog is full. Explain what&#39;s happening at Layer 4 and how you <br>would mitigate it without taking the service offline.<br><br>10. A product team at Meta wants to migrate a real-time video streaming <br>feature from TCP to UDP-based QUIC. Your manager asks you to evaluate the <br>trade-offs at the transport layer and list the specific operational <br>concerns you&#39;d raise before approving the migration.<br><br>11. An internal service at Meta is sending data to a consumer that processes <br>it slowly. Engineers notice the producer&#39;s send rate has throttled to near <br>zero even though the network is healthy. There&#39;s no packet loss. <br>What Layer 4 mechanism explains this, and how would you investigate and fix it?<br><br>12. Meta is running a fleet of NAT gateways for egress traffic. <br>Engineers report intermittent connection failures to external API <br>roughly 0.1% of requests fail with connection refused or timeout. <br>The network path is healthy and packet loss is zero. <br>How do you approach this at Layer 4?<br><br>13. During a code review, a colleague proposes setting the IP TTL to 1 <br>for all packets sent between two services in the same datacenter as a <br>&#39;security measure to prevent routing outside the DC.&#39; <br>You&#39;re asked to evaluate this. What Layer 4 and network interactions <br>does this affect, and would you approve it?<br><br>14. A Meta service migrates from TLS 1.2 to TLS 1.3. <br>After rollout, engineers observe that connection setup latency <br>dropped measurably, but a small number of enterprise clients start <br>failing to connect. Explain the Layer 4 behavior change and why some <br>clients might break.</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=35b4d939b261" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Layer 3 — NETWORK LAYER]]></title>
            <link>https://medium.com/@tales-with-ck/layer-3-network-layer-56eee910ce53?source=rss-6a4c7355f365------2</link>
            <guid isPermaLink="false">https://medium.com/p/56eee910ce53</guid>
            <category><![CDATA[networking]]></category>
            <category><![CDATA[faang-interview-prep]]></category>
            <category><![CDATA[faang]]></category>
            <dc:creator><![CDATA[tales-with-ck]]></dc:creator>
            <pubDate>Fri, 27 Mar 2026 21:11:33 GMT</pubDate>
            <atom:updated>2026-03-31T08:17:12.919Z</atom:updated>
            <content:encoded><![CDATA[<p>I will walk you through this layer so understand what you need to ensure that you have a complete grasps of it.</p><ol><li>Routers and the Internet Protocol</li><li>Network gateway</li><li>Route tables</li><li>IPv4</li><li>IPv6</li><li>Differences in both IPv4 and IPv6</li><li>Static routing</li><li>Dynamic routing</li><li>Backup routes</li><li>L3 switches</li><li>Routing vs switching</li><li>NAT (Network Address Translation)</li><li>Differences in RIP, OSPF, BGP</li><li>MTU (Maximum Transmission Unit)</li><li>Interview scenarios</li></ol><h3>Routers</h3><p>Connects multiple networks together and forwards data packets between them.</p><p>Use routing tables and protocols to determine the best path for data to travel from its source to its destination.</p><p>When a packet arrives at one of the interfaces, the router reads the destination IP address and checks its routing table to determine the next hop.</p><p>Have the ability to fragment and reassemble IP packets.</p><p>Use dynamic routing protocols to automatically update and share routing information with other routers on the network.</p><p>Provide features such as NAT , Quality of service (QoS) and VPN support.</p><h3>Network gateway</h3><p>It acts as a bridge between 2 networks , allowing devices on one network to communicate with devices on another network.</p><h3>Route tables</h3><p>A routing table is a data structure used by routers to determine the best path for forwarding packets to their destination network.</p><p>Contains information about directly connected networks, remote networks learned through static and dynamic protocols and the next-hop router or interface to reach each network.</p><p>Determines the outgoing interface or next-hop Ip address for a packet on the destination IP address.</p><h3>Internet Protocol (IP)</h3><p>Its a network layer protocol that is responsible for routing packets of dta across a network.</p><p>Its protocol that provides the internet its name.</p><p>IP is connectionless, best effort protocol.</p><p>There are 2 versions : version 4 and version 6</p><p>Its an end to end protocol hence responsible for delivering packets of data from the source device to the destination device, regardless of the number of networks that the data traverses.</p><h3>IPv4</h3><p>Its the most widely used version of the IP(Internet Protocol).</p><p>It uses 32 bits addresses which allow a total of approximately 4.3 billion unique addresses.</p><p>They are not globally unique and not intended to be routed on the internet.</p><p>They are mainly used for internal networks and are not reachable from the internet.</p><p>Here are the ranges : — <br>- 10.0.0.0–10.255.255.255 (10.0.0.0/8)<br>- 172.16.0.0–172.31.255.255 (172.16.0.0/12)<br>- 192.168.0.0–192.168.255.255 (192.168.0.0/16)</p><p>IPv4 Header : has a length of 20 -60 bytes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cO5irAIuUhV8y1HaBV2Kfg.png" /><figcaption>IPv4 header</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uzrmmKofFYcr_wczVFCYuQ.png" /></figure><h3><strong>IPv6</strong></h3><p>It was developed to address the IPv4 address shortage.</p><p>It uses 128 bit addresses allowing a total of approximately 3.4 x 10 ^ 38 unique addresses.</p><p>It has an improved routing and security features.</p><p>IPv6 has a fixed header at 40 bytes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HdurQCzSofMtebxkynoStA.png" /><figcaption>IPv6 header</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TgB3MXYpeoCRxKQx76lkgw.png" /></figure><h3><strong>Differences in IPv4 and IPv6</strong></h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*G-hFsPwqhvgW3fa8q1KBwA.png" /></figure><h3>Static routing protocol</h3><p>Manually configuring the routing table on a network router or gateway device.</p><p>The network administrator specifies the route for each network or subnet that the router needs to know how to reach manually by entering the IP address of the destination netwotk along with the next hop IP address.</p><p>Static routing table determines the best path for packets to travel.</p><h3>Dynamic routing protocol</h3><p>Its a protocol that does automatic updates of the routing tables in response to the changes in the network topology.</p><p>It improves network efficiency, reduce network downtime and simplify network management.</p><p>Types : —</p><ol><li><strong>Distance vector</strong> — protocols determine the best path to a destination based on a measure of distance such as hop count. Example RIP and IGRP</li><li>Link state — Best path is based on the state of links in the network. Example OSPF and IS — IS</li><li>Path vector — Best path to a destination is based on information received from other routers. Examples BGP</li></ol><p><strong>Differences in static and dynamic routing</strong></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3klHiNwb2SiBFtwuDsowkw.png" /><figcaption>Differences in static and dynamic routing</figcaption></figure><p><strong>Administrative distance — </strong>Its a value assigned to a routing protocol to indicate its trustworthiness or reliability relative to other routing protocols.</p><p>Used by routers to determine the best path to a destination when there are multiple routing protocols advertising routes to the same destination.</p><p><strong>Backup route</strong> — Its a secondary path to a destination nlw that is used when primary path is unavailable or fails.</p><p>Typically configured in routing protocols using metrics or administrative distances to determine the best path to take.</p><p>Configuring a backup route:-</p><ul><li>Manually add a static route with a higher administrative distance value than the primary route</li><li>Configure a backup interface for a dynamic routing protocol such as OSPF or EIGRP</li><li>Use a routing protocol with load balancing capabilities such as Equal Cost Multipath (ECMP)</li></ul><h4>L3 SWITCHES</h4><p>It combines the functions of a switch and a router allowing them to route traffic between different subnets.</p><p>Operate at both layer 2 and layer 3.</p><p>They use hardware-based switching and routing which provides faster performance compared to software based routing used by traditional routers.</p><p>Advanced features such as access control lists (ACLs), Quality of Service (QoS) settings and support for routing protocols such as BGP and OSPF.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2i2QM7Dp4WArXXKkXdw3pA.png" /><figcaption>Differences in routing and switching</figcaption></figure><h4>NAT (Network Address Translation)</h4><p>It translates private IP addresses into public IP addresses within a local network for internet communication. It maps private IP addresses to public IP addresses when traffic flows.</p><p>Addresses the shortage of available IP addresses and enhance network security by concealing internal IP addresses from external sources.</p><p>Configured on network devices such as routers, firewalls and gateways that serve as intermediaries between a private network and external networks.</p><p>Types of NAT:</p><ul><li><strong>Static NAT </strong>— It involves one to one mapping between a private IP address and a public address.</li><li><strong>Dynamic NAT</strong> — Allows a pool of public IP addresses to be shared among multiple devices within a private network.</li><li><strong>Port Address Translation (PAT)</strong> — maps multiple private IP addresses to a single public IP address using different source port numbers.</li></ul><p><strong>NAT66</strong>: IPv6 method that translates addresses similar to IPv4 NAT. Converts global IPv6 addresses to local ones.</p><p><strong>NAT64</strong>: Used for transition from IPv4 to IPv6. Enables IPv6 only devices to communicate with IPv4 only devices by translating between the 2 protocols.</p><h4>RIP, OSPF, BGP</h4><p>I have done a separate document that I go in detail concerning OSPF and BGP since they are core in an interview setting.</p><p>I will share a table that shows the core differences among the 3 and you can reference the other articles to get a deep dive of the other core parts.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*h_Ca0_R7ygpRq7pkBw9cfQ.png" /><figcaption>RIP vs OSPF vs BGP</figcaption></figure><h4><strong>MTU (Maximum Transmission Unit)</strong></h4><p>Maximum size of a single packet that can be transmitter over a particular network link or protocol.</p><p>Measured in bytes.</p><p>MTU-related issues can cause network connectivity problems, especially when traversing different network technologies or VPNs. In such cases, adjusting the MTU size or enabling Path MTU Discovery can help resolve the problems.</p><h3>Interview scenarios</h3><pre>What is your favourite L2/L3 protocol?<br><br>Why can&#39;t we use OSPF to connect all systems together, <br>and what is unique about BGP? What is an autonomous system? <br>How is routing information exchanged? What&#39;s the difference <br>between iBGP and eBGP peers? How does BGP differentiate between <br>external and internal peers? How is a BGP session established? <br>How do you prevent routing loops? How do you control inbound/outbound <br>traffic? Is there any inspection before sending a route to a different <br>neighbour, by the sender or receiver?<br><br>BGP vs OSPF — differences, explain in depth. <br>Network fundamentals — routing, switching, layering.<br><br>Describe how the TCP header in an IPv4 packet changes going <br>from device to device, and which fields change. Describe the same for IPv6.<br><br>Given a scenario of experiencing network performance issues such as <br>latency and packet loss , what are the possible reasons? <br>What steps would you take to narrow down the issue?<br><br>Walk me through how you&#39;d troubleshoot a server that&#39;s not reachable.<br><br></pre><p>Happy coding and have a great one.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=56eee910ce53" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[PYTHON ROADMAP]]></title>
            <link>https://medium.com/@tales-with-ck/python-roadmap-ca2f1f74460f?source=rss-6a4c7355f365------2</link>
            <guid isPermaLink="false">https://medium.com/p/ca2f1f74460f</guid>
            <category><![CDATA[basics]]></category>
            <category><![CDATA[python-programming]]></category>
            <dc:creator><![CDATA[tales-with-ck]]></dc:creator>
            <pubDate>Fri, 06 Mar 2026 12:15:52 GMT</pubDate>
            <atom:updated>2026-03-06T12:15:52.397Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7VjCzGhFYWm2W8mEEvDDkQ.png" /><figcaption>Python</figcaption></figure><p>If I was starting all over again to learn python this are the core things I would focus on and ensure that I have then in my finger tips.</p><p><strong>BASICS ARE THE ICING IN THE CAKE</strong>.</p><ol><li>Introduction</li><li>Strings</li><li>Arrays</li><li>Logic and operators</li><li>Conditional statements</li><li>Loops</li><li>Data structures</li><li>Functions</li></ol><h3>Introduction</h3><ol><li>What is python</li><li>How python works</li><li>Why learn python</li><li>Variables</li><li>OOP — Object Oriented Programming in Python</li><li>Print statements</li><li>input()</li><li>Data types</li><li>Comments</li></ol><h3>Strings</h3><ol><li>String operators</li><li>Built in types and methods</li><li>Indexing</li><li>Slicing</li><li>Searching and validating</li></ol><h3>Arrays</h3><ol><li>Array methods and operators</li><li>Array functions</li><li>Comprehensions</li><li>Slicing</li></ol><h3>Logic and operators</h3><ol><li>Membership operators</li><li>Comparison operators</li><li>Logical operators</li><li>Identity operator</li></ol><h3>Conditional statements</h3><ol><li>If, else, elif, nested if, ternary and match cases</li></ol><h3>Loops</h3><ol><li>For loop — for loop, statements and nested loops</li><li>While loop</li></ol><h3>Data structures</h3><ol><li>Lists</li><li>Dictonaries (hashmaps)</li><li>Sets</li><li>Tuples</li></ol><h3>Functions</h3><ol><li>Types of functions</li><li>Parameters</li><li>Arguments</li><li>Return</li><li>Types by purpose</li><li>Clean functions</li></ol><p>Happy coding and I hope python becomes your favorite language.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ca2f1f74460f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[BACKTRACKING]]></title>
            <link>https://medium.com/@tales-with-ck/backtracking-9e09a6c6d3f5?source=rss-6a4c7355f365------2</link>
            <guid isPermaLink="false">https://medium.com/p/9e09a6c6d3f5</guid>
            <category><![CDATA[backtracking]]></category>
            <dc:creator><![CDATA[tales-with-ck]]></dc:creator>
            <pubDate>Wed, 04 Feb 2026 14:54:00 GMT</pubDate>
            <atom:updated>2026-02-04T14:54:00.037Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/696/1*Vg1IyrEyIv1j-AJO8ZSrpA.png" /><figcaption>Backtracking</figcaption></figure><p>It means ‘Try a choice, explore where it leads, then undo that choice and try another’.</p><p>Key ideas:-</p><ul><li>You build a solution step by step</li><li>At each step, you have choices</li><li>You revert (backtrack) after exploring a choice</li></ul><p>According to wikipedia:-</p><p><strong>Backtracking</strong> is a general algorithm for finding all (or some) solutions to some computational problems, that incrementally builds candidates to the solutions, and abandons each partial candidate (“backtracks”) as soon as it determines that the candidate cannot possibly be completed to a valid solution.</p><p>A nice example to work with is the maze. Imagine to have a maze and you want to find if it has an exit. You can pick a path and realize it wont take you out hence you have to take another one.</p><p>A simple pseudo code for traversing a maze and check if there is an exit.</p><pre>function backtrack(cell):<br>  <br>  if is_exit:<br>    return true<br>  for each direction of cell:<br>    if backtrack(next_cell):<br>      return true    <br>  <br>  return false<br><br># Try a path<br># If it leads to success - stop<br># Otherwise - undo and try another path</pre><p>Backtracking is almost always implemented with recursion.</p><p>In most backtracking problems, you will be building something, either directly (like modifying an array) or indirectly (using variables to represent some state).</p><p>Pseudocode for a general backtracking</p><pre>// let curr represent the thing you are building<br>// it could be an array or a combination of variables<br><br>function backtrack(curr) {<br>    if (base case) {<br>        Increment or add to answer<br>        return<br>    }<br><br>    for (iterate over input) {<br>        Modify curr<br>        backtrack(curr)<br>        Undo whatever modification was done to curr<br>    }<br>}<br><br>curr<br>- The partial solution you’re building<br>(subset, path, string, sequence, coordinates)<br><br>Base case<br>- “Am I done building one valid solution?”<br><br>Add to answer<br>- Save this solution (or count it, or return true)<br><br>Loop over choices<br>- Try every possible next decision<br><br>Modify curr<br>- Make a choice<br><br>Recursive call<br>- Explore deeper with that choice<br><br>Undo modification<br>- Backtrack so the next choice starts clean</pre><p>Each call to the function backtrack represents a node in the tree. Each iteration in the for loop represents a child of the current node, and calling backtrack in that loop represents moving to a child.</p><p>The line where you undo the modifications is the “backtracking” step and is equivalent to moving back up the tree from a child to its parent.</p><p>At any given node, the path from the root to the node represents a candidate that is being built. The leaf nodes are complete solutions and represent when the base case is reached. The root of this tree is an empty candidate and represents the scope that the original backtrack call is being made from.</p><p>Problems to solve :-</p><ul><li>Subsets</li><li>Permutations</li><li>Combinations</li></ul><p>And their variants to help you get a grasp of them.</p><p>I hope this helps you ensure you have a deep understanding of backtracking.</p><p>Happy coding.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9e09a6c6d3f5" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>