Algorithm exercises: divide-and-conquer#

Learning objectives

By the end of this lab, you will be able to:

  • Understand the difference between simple recursion and divide-and-conquer

  • Apply divide-and-conquer to solve optimization problems


Part 1: Divide-and-conquer#

In Divide and conquer algorithms, you learned about the divide-and-conquer (D&C) paradigm. Now it is time to apply it. D&C is a special form of recursion where you:

  1. Divide the problem into smaller subproblems (usually two halves)

  2. Conquer each subproblem recursively

  3. Combine the results of the subproblems

The key difference from simple recursion is that D&C typically divides the input in half (or similar proportions), rather than removing one element at a time. This often leads to more efficient algorithms.

Simple recursion vs divide-and-conquer#

With simple recursion, we remove one element at a time:

Simple recursion:
    f([1,2,3,4,5,6,7,8])
    └─▶ f([2,3,4,5,6,7,8])
        └─▶ f([3,4,5,6,7,8])
            └─▶ f([4,5,6,7,8])
                └─▶ ... and so on

    → 8 elements = 8 recursive calls

With divide-and-conquer, we split the problem in half each time:

Divide-and-conquer:
    f([1,2,3,4,5,6,7,8])         ← Level 1: full list
    │
    ├─▶ f([1,2,3,4])             ← Level 2: two halves
    │   ├─▶ f([1,2])             ← Level 3: quarters
    │   └─▶ f([3,4])
    │
    └─▶ f([5,6,7,8])
        ├─▶ f([5,6])
        └─▶ f([7,8])

    → 8 elements = only 3 levels deep

The difference matters for large inputs: processing 1000 elements with simple recursion requires 1000 calls, but D&C only needs about 10 levels (since we keep halving: 1000 → 500 → 250 → 125 → …).

Exercise 1.1: Find the longest word in a text#

You are analyzing a literary text and want to find the longest word. Using divide-and-conquer, you can split the text, find the longest word in each half, and compare them.

This pattern is useful in text analysis: instead of scanning every word sequentially, D&C lets you process large texts by breaking them into manageable pieces.

print(longest_word_dc("To be or not to be"))
# Expected: "not" (3 letters, first among equals)

print(longest_word_dc("It was the best of times"))
# Expected: "times" (5 letters)

print(longest_word_dc("Nevermore said the raven"))
# Expected: "Nevermore" (9 letters)

print(longest_word_dc("hello"))
# Expected: "hello"

print(longest_word_dc(""))
# Expected: ""

Exercise 1.2: Count character appearances in a text#

You are analyzing a literary text and want to count how many times a specific character (like a letter or punctuation mark) appears. Use divide-and-conquer: split the text in half, count in each half, combine the results.

This mirrors how large-scale text analysis works: divide a massive corpus into chunks, process each chunk, aggregate results.

text = "To be, or not to be, that is the question"

print(count_char_dc(text, "o"))    # Expected: 4
print(count_char_dc(text, "t"))    # Expected: 5
print(count_char_dc(text, ","))    # Expected: 2
print(count_char_dc(text, "z"))    # Expected: 0
print(count_char_dc("", "a"))      # Expected: 0
print(count_char_dc("aaa", "a"))   # Expected: 3