Python Algorithms & Data Structures track

Lesson 22 · Part 6: DP & Greedy

Greedy Algorithms

Meeting-room scheduling, coin change, activity selection and correctness proofs

Part 6: DP & Greedy3 min readFree lesson

Introduction

A greedy algorithm is a method that finds the overall optimal solution by making the choice that looks best at each step.

"The choice that is best at the moment ultimately leads to the best overall."

Difference from DP:

  • DP: considers all possibilities, stores previous results
  • Greedy: does not backtrack, only chooses the current best
Conditions for Greedy to be Optimal (Matroid Theory)
─────────────────────────────────────────────────────
1. Greedy Choice Property:
A locally optimal choice is part of the global optimum

2. Optimal Substructure:
The overall optimum is composed of the optima of subproblems
─────────────────────────────────────────────────────
→ Greedy guarantees optimality only when these two conditions hold!

Learning Goals

  1. Understand how to prove the correctness of greedy algorithms using the exchange argument.
  2. The activity selection problem (meeting room allocation) can be solved in O(n log n) by sorting based on finish times.
  3. Recognize cases where greedy fails and distinguish the criteria for applying DP and greedy.

Key Concepts

Exchange Argument

Greedy proof method:
1. Assume the optimal solution is different from the greedy solution
2. Swap the parts in the optimal solution that are different from what the greedy chooses
3. Show that the solution does not get worse after the swap
4. Therefore, greedy solution ≥ optimal solution → greedy is optimal

Activity Selection

Meetings: [(start, end)] Sorting criterion = end time!

Why use the end time as the criterion?
The earlier an activity ends, the more subsequent activities can be selected.

Example:
Activities: [(1,4), (3,5), (0,6), (5,7), (3,9), (5,9), (6,10), (8,11), (8,12), (2,14), (12,16)]
Sorted: [(1,4), (3,5), (0,6), (5,7), ...]
Selection: (1,4) → (5,7) → (8,11) → (12,16) = 4 activities

Time complexity: O(n log n) — sorting dominates

Cases Where Greedy Fails

Example of coin change:
Coins [1, 5, 6, 9], Amount 11
Greedy: 9 + 1 + 1 = 3 coins
Optimal: 5 + 6 = 2 coins ← Greedy fails!

∵ Greedy choice property does not hold

Complexity Comparison

┌──────────────────────────┬──────────────┬──────────────┐
│ Problem                  │ Greedy       │ DP           │
├──────────────────────────┼──────────────┼──────────────┤
│ Activity Selection        │ O(n log n)   │ O(n²)        │
│ Korean Coin Change        │ O(k)         │ O(n × amount)│
│ Huffman Coding            │ O(n log n)   │ O(n log n)   │
│ Minimum Number of Meeting Rooms │ O(n log n)   │ O(n log n)   │
└──────────────────────────┴──────────────┴──────────────┘

Example

File Contents
src/01_activity_selection.py Meeting Room Allocation (Activity Selection)
src/02_coin_greedy.py Coin Change, Example of Failure
src/03_meeting_rooms.py Minimum Number of Meeting Rooms (Heap)
src/04_greedy_examples.py Change, Maximum Product, Array Sum

Common Mistakes

  1. Skipping Greedy Correctness Proof: Just apply greedy if it seems correct — exchange argument or counterexample exploration is necessary
  2. Sorting Criteria Error: For activity selection, should sort by finish time, not start time — sorting by start time is incorrect
  3. Misuse of Greedy in Coin Problems: Greedy fails if coin multiples are not exponential — Korean coins work because they are 1, 5, 10, 50, 100, 500
  4. Timing for Using Heaps: To minimize the number of meeting rooms, you need a min-heap (based on ending times) — without a priority queue, it’s O(n²)
  5. Error in Mixing Greedy + DP: Applying greedy to a problem that cannot be solved greedily → first find counterexamples

Summary

  • Greedy = best choice at each step + greedy choice property + optimal substructure
  • Justification is proven by exchange argument
  • Activity selection: sort by end time → O(n log n)
  • When greedy fails → switch to DP or backtracking

Try It Yourself

  1. Implement Huffman coding: generate minimum-bit codes based on frequency
  2. Minimum spanning tree (Kruskal): sort edges by weight and choose greedily
  3. Fractional Knapsack problem: greedy based on Value/Weight ratio

Example files

The runnable files used in this lesson. Copy them into your own project and experiment.

src/01_activity_selection.pyPython
#Topic: Activity Selection Problem (Meeting Room Assignment) — Greedy
# Time: O(n log n)   Space: O(n)


def activity_selection(activities: list[tuple[int, int]]) -> list[tuple[int, int]]:
    """Select the maximum number of activities (so that they do not overlap).

    Key: Sort in ascending order based on end time
    → Select activities that end earlier first

    Time: O(n log n) — sorting dominates
    Space: O(n)"""
    #Sort by end time
    sorted_activities = sorted(activities, key=lambda x: (x[1], x[0]))

    selected: list[tuple[int, int]] = []
    last_end = -1   #End time of the last selected activity

    for start, end in sorted_activities:
        if start >= last_end:   #No overlap
            selected.append((start, end))
            last_end = end

    return selected


def activity_selection_with_idx(
    activities: list[tuple[int, int]]
) -> tuple[int, list[int]]:
    """Return the maximum number of activities + the indices selected.

    Time: O(n log n)
    Space: O(n)"""
    indexed = sorted(enumerate(activities), key=lambda x: x[1][1])
    selected_idx: list[int] = []
    last_end = -1

    for idx, (start, end) in indexed:
        if start >= last_end:
            selected_idx.append(idx)
            last_end = end

    return len(selected_idx), selected_idx


def max_non_overlapping(intervals: list[tuple[int, int]]) -> int:
    """LeetCode style: Maximum number of non-overlapping intervals.

    Time: O(n log n)
    Space: O(1)"""
    sorted_intervals = sorted(intervals, key=lambda x: x[1])
    count = 0
    last_end = float("-inf")

    for start, end in sorted_intervals:
        if start >= last_end:
            count += 1
            last_end = end

    return count


def min_removals_for_non_overlap(intervals: list[tuple[int, int]]) -> int:
    """How many should be removed to avoid overlaps?

    Time: O(n log n)
    Space: O(1)"""
    total = len(intervals)
    keep = max_non_overlapping(intervals)
    return total - keep


#── Example Execution ──────────────────────────────────────────────────────────────
if __name__ == "__main__":
    #Baekjoon 1931 type data
    activities = [
        (1, 4), (3, 5), (0, 6), (5, 7),
        (3, 9), (5, 9), (6, 10), (8, 11),
        (8, 12), (2, 14), (12, 16),
    ]

    print("=== Activity Selection ===")
    print(f"Total activities: {len(activities)}")
    selected = activity_selection(activities)
    print(f"Selected activities ({len(selected)}):")
    for s, e in selected:
        print(f"  [{s:2d}, {e:2d}]")

    print("\n=== Timeline Visualization ===")
    selected_set = set(selected)
    print("Time:" + "".join(f"{i:2d}" for i in range(17)))
    for s, e in sorted(activities):
        bar = "  " * s + "━━" * (e - s)
        marker = " ✓" if (s, e) in selected_set else "  "
        print(f"{marker}  [{s},{e}]: {bar}")

    print("\n=== Minimal Removal of Overlapping Intervals ===")
    intervals = [(1, 2), (2, 3), (3, 4), (1, 3)]
    print(f"Intervals: {intervals}")
    print(f"Maximum number of non-overlapping: {max_non_overlapping(intervals)}")
    print(f"Minimum number of removals:     {min_removals_for_non_overlap(intervals)}")

    print("\n=== Comparison of Sorting Criteria (Start Time vs End Time) ===")
    wrong_case = [(0, 10), (1, 2), (3, 4)]
    by_start = sorted(wrong_case, key=lambda x: x[0])
    by_end = sorted(wrong_case, key=lambda x: x[1])

    print(f"Original: {wrong_case}")
    print(f"Sorted by start time: {by_start}")
    print(f"Sorted by end time: {by_end}")

    #Greedy based on start time (wrong)
    wrong_result = []
    last = -1
    for s, e in by_start:
        if s >= last:
            wrong_result.append((s, e))
            last = e
    print(f"Greedy result by start time: {wrong_result} (wrong!)")

    correct = activity_selection(wrong_case)
    print(f"Greedy result by end time: {correct} (correct)")
src/02_coin_greedy.pyPython
#Topic: Coin Change — Greedy (When It Is Optimal and When It Fails)
#Time: O(k) Greedy — k = number of coin types
# Space: O(1)


def coin_greedy(coins: list[int], amount: int) -> tuple[int, list[int]]:
    """Greedy Coin Change (use larger coins first).

    Time:  O(k log k + k) = O(k log k)
    Space: O(k) to store results"""
    coins_sorted = sorted(coins, reverse=True)
    result: list[int] = []
    remaining = amount

    for coin in coins_sorted:
        while remaining >= coin:
            result.append(coin)
            remaining -= coin

    return len(result) if remaining == 0 else -1, result


def coin_dp(coins: list[int], amount: int) -> tuple[int, list[int]]:
    """DP Coin Change (guaranteed optimal).

    Time:  O(amount × k)
    Space: O(amount)"""
    INF = float("inf")
    dp = [INF] * (amount + 1)
    dp[0] = 0
    parent = [-1] * (amount + 1)

    for i in range(1, amount + 1):
        for coin in coins:
            if coin <= i and dp[i - coin] + 1 < dp[i]:
                dp[i] = dp[i - coin] + 1
                parent[i] = coin

    if dp[amount] == INF:
        return -1, []

    #Backtracking
    result: list[int] = []
    curr = amount
    while curr > 0:
        result.append(parent[curr])
        curr -= parent[curr]

    return int(dp[amount]), sorted(result, reverse=True)


def is_greedy_optimal(coins: list[int], max_amount: int = 1000) -> bool:
    """Check if the greedy approach is always optimal for a given set of coins.

    Time:  O(max_amount × k²)
    Space: O(max_amount)"""
    for amount in range(1, max_amount + 1):
        g_count, _ = coin_greedy(coins, amount)
        d_count, _ = coin_dp(coins, amount)
        if g_count != d_count and g_count != -1:
            return False
    return True


#── Example Execution ──────────────────────────────────────────────────────────────
if __name__ == "__main__":
    print("=== Korean Coins (Greedy Optimal) ===")
    korean = [500, 100, 50, 10, 5, 1]
    test_amounts = [1260, 730, 999, 376]
    for amt in test_amounts:
        g_cnt, g_coins = coin_greedy(korean, amt)
        d_cnt, d_coins = coin_dp(korean, amt)
        status = "Match ✓" if g_cnt == d_cnt else "Mismatch ✗"
        print(f"{amt:4d} won: Greedy={g_cnt} coins {g_coins}, DP={d_cnt} coins → {status}")

    print("=== Greedy Failure Examples ===")
    bad_coins = [1, 5, 6, 9]
    for amt in [10, 11, 12]:
        g_cnt, g_coins = coin_greedy(bad_coins, amt)
        d_cnt, d_coins = coin_dp(bad_coins, amt)
        status = "Match ✓" if g_cnt == d_cnt else "Greedy Fails ✗"
        print(f"Coins={bad_coins}, {amt} won:")
        print(f"Greedy: {g_cnt} coins {g_coins}")
        print(f"DP:     {d_cnt} coins {d_coins} → {status}")

    print("=== Analysis of Greedy Optimal Conditions ===")
    coin_sets = [
        ([1, 5, 10, 50, 100, 500], "Korean Coins"),
        ([1, 5, 6, 9], "Greedy Fails"),
        ([1, 2, 5, 10, 20, 50, 100, 200], "Euro Coins"),
        ([1, 3, 4], "Failure Example 2"),
    ]
    for coins, name in coin_sets:
        optimal = is_greedy_optimal(coins, 100)
        print(f"{name:15s} {coins}: Greedy Optimal? {optimal}")
src/03_meeting_rooms.pyPython
#Topic: Minimum Meeting Rooms
# Time: O(n log n)   Space: O(n)

import heapq


def min_meeting_rooms(intervals: list[tuple[int, int]]) -> int:
    """Minimum number of meeting rooms — using a min heap.

    Idea:
      - Sort start times
      - Store each meeting's end time in a min heap
      - If a new meeting does not overlap with the meeting that ends the earliest, reuse the room

    Time:  O(n log n)
    Space: O(n)"""
    if not intervals:
        return 0

    sorted_intervals = sorted(intervals, key=lambda x: x[0])
    heap: list[int] = []   #End time of each meeting room

    for start, end in sorted_intervals:
        if heap and heap[0] <= start:
            #Reuse the meeting room that ends the earliest
            heapq.heapreplace(heap, end)
        else:
            #Need a new meeting room
            heapq.heappush(heap, end)

    return len(heap)


def min_meeting_rooms_two_pointer(
    intervals: list[tuple[int, int]]
) -> int:
    """Minimum number of meeting rooms — two pointers method.

    Sort start times and end times separately
    → Maximum number of meetings happening simultaneously at any moment

    Time:  O(n log n)
    Space: O(n)"""
    if not intervals:
        return 0

    starts = sorted(s for s, _ in intervals)
    ends = sorted(e for _, e in intervals)

    rooms = 0
    max_rooms = 0
    j = 0   #ends pointer

    for i in range(len(starts)):
        if starts[i] < ends[j]:
            rooms += 1
            max_rooms = max(max_rooms, rooms)
        else:
            rooms -= 1   #One ended
            j += 1

    return max_rooms


def meeting_rooms_schedule(
    intervals: list[tuple[int, int]]
) -> list[list[tuple[int, int]]]:
    """Return the list of meetings assigned to each meeting room.

    Time:  O(n log n)
    Space: O(n)"""
    if not intervals:
        return []

    indexed = sorted(enumerate(intervals), key=lambda x: x[1][0])
    #(end time, meeting room number)
    heap: list[tuple[int, int]] = []
    rooms: list[list[tuple[int, int]]] = []

    for orig_idx, (start, end) in indexed:
        if heap and heap[0][0] <= start:
            end_time, room_idx = heapq.heappop(heap)
            rooms[room_idx].append((start, end))
            heapq.heappush(heap, (end, room_idx))
        else:
            room_idx = len(rooms)
            rooms.append([(start, end)])
            heapq.heappush(heap, (end, room_idx))

    return rooms


#── Example Execution ──────────────────────────────────────────────────────────────
if __name__ == "__main__":
    meetings = [
        (0, 30), (5, 10), (15, 20), (2, 12), (11, 25),
    ]

    print("=== Meeting List ===")
    print(f"{'Meeting':>5} | {'Start':>4} | {'End':>4}")
    print("-" * 18)
    for i, (s, e) in enumerate(meetings):
        print(f"{i:>5} | {s:>4} | {e:>4}")

    min_rooms = min_meeting_rooms(meetings)
    min_rooms_tp = min_meeting_rooms_two_pointer(meetings)
    print(f"Minimum Meeting Rooms (Heap):       {min_rooms}")
    print(f"Minimum Meeting Rooms (Two Pointers): {min_rooms_tp}")
    assert min_rooms == min_rooms_tp

    print("=== Meeting Room Assignment ===")
    schedule = meeting_rooms_schedule(meetings)
    for room_idx, room_meetings in enumerate(schedule):
        print(f"Meeting Room {room_idx + 1}: {room_meetings}")

    print("\n=== Timeline Visualization ===")
    max_time = max(e for _, e in meetings)
    time_header = "Time:" + "".join(f"{t:3d}" for t in range(0, max_time + 1, 3))
    print(f"  {time_header}")
    for i, (s, e) in enumerate(sorted(meetings)):
        bar = "   " * (s // 3) + "━━━" * ((e - s) // 3 + 1)
        print(f"Meeting [{s},{e}]: {bar}")

    print("=== Additional Tests ===")
    test_cases = [
        [(7, 10), (2, 4)],
        [(0, 30), (5, 10), (15, 20)],
        [(1, 5), (2, 6), (3, 7)],
    ]
    for tc in test_cases:
        r = min_meeting_rooms(tc)
        print(f"{tc} → {r} meeting rooms")
src/04_greedy_examples.pyPython
#Topic: Greedy Example Collection — Change, Maximum Product, Maximum Array Sum
#Practice various greedy patterns

import heapq


def min_coins_korean(amount: int) -> int:
    """Minimum number of coins as change with Korean coins (greedy optimal).

    Time:  O(1) — fixed coin types
    Space: O(1)"""
    coins = [500, 100, 50, 10, 5, 1]
    count = 0
    for coin in coins:
        count += amount // coin
        amount %= coin
    return count


def max_product_of_two(arr: list[int]) -> int:
    """Maximum product of two numbers in an array.

    Cases: product of the two largest positive numbers vs product of the two smallest negative numbers (negative×negative=positive)

    Time:  O(n)
    Space: O(1)"""
    if len(arr) < 2:
        return 0

    max1 = max2 = float("-inf")   #The two largest numbers
    min1 = min2 = float("inf")    #The two smallest numbers (negative)

    for x in arr:
        if x >= max1:
            max1, max2 = x, max1
        elif x > max2:
            max2 = x

        if x <= min1:
            min1, min2 = x, min1
        elif x < min2:
            min2 = x

    return max(max1 * max2, min1 * min2)


def maximize_array_sum_with_swaps(
    arr: list[int], b: list[int], k: int
) -> int:
    """Maximize the sum of array arr — can replace elements with elements from b up to k times.

    Strategy: replace small elements from arr with large elements from b

    Time:  O(n log n)
    Space: O(n)"""
    arr_sorted = sorted(arr)
    b_sorted = sorted(b, reverse=True)

    for i in range(k):
        if arr_sorted[i] < b_sorted[i]:
            arr_sorted[i] = b_sorted[i]
        else:
            break

    return sum(arr_sorted)


def gas_station(gas: list[int], cost: list[int]) -> int:
    """Gas station problem: find a starting point to make a full circuit.

    Greedy: if total fuel >= total cost, a solution always exists
    Starting point = next point immediately after the tank goes negative

    Time:  O(n)
    Space: O(1)"""
    total_tank = 0
    current_tank = 0
    start = 0

    for i in range(len(gas)):
        diff = gas[i] - cost[i]
        total_tank += diff
        current_tank += diff

        if current_tank < 0:
            start = i + 1
            current_tank = 0

    return start if total_tank >= 0 else -1


def jump_game(nums: list[int]) -> bool:
    """Jump game: can you reach the last position from the first?

    Greedy: keep updating the current maximum reachable position

    Time:  O(n)
    Space: O(1)"""
    max_reach = 0
    for i, jump in enumerate(nums):
        if i > max_reach:
            return False
        max_reach = max(max_reach, i + jump)
    return True


def jump_game_min_jumps(nums: list[int]) -> int:
    """Reach the end with the minimum number of jumps.

    Time:  O(n)
    Space: O(1)"""
    jumps = 0
    current_end = 0
    farthest = 0

    for i in range(len(nums) - 1):
        farthest = max(farthest, i + nums[i])
        if i == current_end:
            jumps += 1
            current_end = farthest
            if current_end >= len(nums) - 1:
                break

    return jumps


#── Example Execution ──────────────────────────────────────────────────────────────
if __name__ == "__main__":
    print("=== Korean Coin Change ===")
    for amt in [4860, 1260, 1000, 5]:
        n = min_coins_korean(amt)
        print(f"{amt:5d} won → {n} coins")

    print("=== Maximum Product of Two Numbers ===")
    test_arrs = [
        [1, 5, 3, 2, 4],
        [-10, -3, 5, 2, -1],
        [-5, -4, 1, 2],
        [0, -1, -2, 3],
    ]
    for arr in test_arrs:
        mp = max_product_of_two(arr)
        #Brute-force check
        brute = max(arr[i] * arr[j] for i in range(len(arr)) for j in range(i+1, len(arr)))
        assert mp == brute, f"{arr}: {mp} vs {brute}"
        print(f"{arr}: Maximum Product = {mp}")
    print("All test cases passed ✓")

    print("=== Maximize Array Sum (k swaps) ===")
    a = [1, 2, 3, 4, 5]
    b = [5, 6, 7, 8, 9]
    for k in range(6):
        result = maximize_array_sum_with_swaps(a, b, k)
        print(f"  k={k}: {result}")

    print("=== Gas Station Problem ===")
    gas_cases = [
        ([1, 2, 3, 4, 5], [3, 4, 5, 1, 2]),
        ([2, 3, 4], [3, 4, 3]),
    ]
    for g, c in gas_cases:
        start = gas_station(g, c)
        print(f"gas={g}, cost={c} → Starting Point: {start}")

    print("=== Jump Game ===")
    jump_cases = [
        ([2, 3, 1, 1, 4], True),
        ([3, 2, 1, 0, 4], False),
        ([2, 3, 0, 1, 4], True),
    ]
    for nums, expected in jump_cases:
        can = jump_game(nums)
        min_j = jump_game_min_jumps(nums) if can else -1
        print(f"{nums}: Can Reach={can} (Expected={expected}), Minimum Jumps={min_j}")

Assignment

Problem 1: Baekjoon 1931 — Meeting Room Assignment (Silver I)

Link: https://www.acmicpc.net/problem/1931

Problem Summary

Given n meetings (start, end), determine the maximum number of meetings that can be held in one meeting room.

Key Approach

  1. Sort in ascending order by end time
  2. If end times are the same, sort in ascending order by start time (important!)
  3. Greedy: If current meeting start >= last selected meeting end → select it

Traps

  • Need to sort by start time when end times are equal
  • Handle start = end (length 0 meetings)

Time/Space Complexity

  • Time: O(n log n), Space: O(n)

Problem 2: Baekjoon 11047 — Coin 0 (Silver I)

Link: https://www.acmicpc.net/problem/11047

Problem Summary

Minimum number of coins to make amount k. Coin denominations are guaranteed to be multiples.

Key Approach

Greedy from the largest coin (Greedy is optimal since coin values are guaranteed multiples)

Time/Space Complexity

  • Time: O(n), Space: O(n)

Problem 3: Baekjoon 1744 — Number Binding (Gold IV)

Link: https://www.acmicpc.net/problem/1744

Problem Summary

Group numbers in a sequence to multiply or just add to achieve the maximum sum. (Multiplication replaces grouping)

Key Approach

  • Positive numbers: multiply the largest together (descending order, in pairs of 2)
  • Negative numbers: multiply the smallest (largest absolute value) together (ascending order, in pairs of 2)
  • 1 is a loss when multiplied → just add
  • 0 and odd number of negatives → pair with 0 to cancel

Time/Space Complexity

  • Time: O(n log n), Space: O(n)

Solution File

  • boj_1931.py
  • boj_11047.py
  • boj_1744.py
Show the reference solution

Try the assignment yourself first — comparing your attempt with a working solution is where most of the learning happens.

boj_11047.pyPython
#Baekjoon 11047 — Coin 0
# https://www.acmicpc.net/problem/11047
# Time: O(n)  Space: O(n)

import sys

input = sys.stdin.readline


def solve() -> None:
    n, k = map(int, input().split())
    coins = [int(input()) for _ in range(n)]

    count = 0
    #Greedy from the largest coin
    for coin in reversed(coins):
        if coin <= k:
            count += k // coin
            k %= coin
        if k == 0:
            break

    print(count)


solve()
boj_1744.pyPython
#Baekjoon 1744 — Number Binding
# https://www.acmicpc.net/problem/1744
# Time: O(n log n)  Space: O(n)

import sys

input = sys.stdin.readline


def solve() -> None:
    n = int(input())
    nums = [int(input()) for _ in range(n)]

    positives = sorted([x for x in nums if x > 1], reverse=True)
    ones = nums.count(1)
    negatives = sorted([x for x in nums if x < 0])
    zeros = nums.count(0)

    total = 0

    #Positive numbers: multiply in pairs from the largest
    i = 0
    while i + 1 < len(positives):
        total += positives[i] * positives[i + 1]
        i += 2
    if i < len(positives):
        total += positives[i]

    #1: just add
    total += ones

    #Negative numbers: multiply in pairs from the smallest (largest absolute value)
    i = 0
    while i + 1 < len(negatives):
        total += negatives[i] * negatives[i + 1]
        i += 2
    if i < len(negatives):
        #Odd number of negatives → if there is a 0, multiply to cancel out; if not, just add
        if zeros == 0:
            total += negatives[i]
        #If zeros > 0, pair with 0 to make 0 (do not add)

    print(total)


solve()
boj_1931.pyPython
#Baekjoon 1931 — Meeting Room Assignment
# https://www.acmicpc.net/problem/1931
# Time: O(n log n)  Space: O(n)

import sys

input = sys.stdin.readline


def solve() -> None:
    n = int(input())
    meetings = [tuple(map(int, input().split())) for _ in range(n)]

    #Sort by end time, if equal then by start time
    meetings.sort(key=lambda x: (x[1], x[0]))

    count = 0
    last_end = 0

    for start, end in meetings:
        if start >= last_end:
            count += 1
            last_end = end

    print(count)


solve()

Learn this lesson with a mentor

Work through the Python Algorithms & Data Structures track on your own, or join a guided cohort at our Pitampura campus and online.

Call NowEnroll Now