DEV Community

Timevolt
Timevolt

Posted on

How to Read Constraints Like Sherlock Holmes: Deduce the Algorithm from the Clues

The Quest Begins (The "Why")

I still remember the first time I stared at a coding challenge and felt my brain short‑circuit. The problem asked for the longest sub‑array with a sum divisible by K. The constraints said N could be as large as 2 × 10⁵ and each element could be up to 10⁹. My instinct was to start nesting loops, but a voice in my head whispered, “That’s going to be O(N²) – you’ll melt the server.” I spent an hour twisting my code, only to watch the timer blow past the limit. Frustrated, I closed the editor and went for a walk, wondering how top‑coders seem to glance at the limits and instantly know whether to reach for a hash map, a sliding window, or a DP table.

That walk turned into a mini‑epiphany: constraints aren’t just boring numbers; they’re clues left by the problem setter, telling you exactly what kind of solution is expected. Once I learned to read them like a detective reads a crime scene, the whole process became a game instead of a grind.

The Revelation (The Insight)

The mental framework I now use boils down to three quick questions:

  1. What’s the scale of the input?

    • N ≤ 10³ → brute force or O(N²) is usually fine.
    • N ≤ 10⁵ → aim for O(N log N) or O(N).
    • N ≤ 10⁶ → you’ll likely need linear or near‑linear time, often with clever hashing or two‑pointer tricks.
    • N > 10⁶ → think about sub‑linear approaches (binary search, math formulas, or preprocessing).
  2. What do the value ranges tell us?

    • Small value ranges (e.g., ≤ 10�({}^{\text{5}})) often hint at counting sort, frequency arrays, or DP on value dimension.
    • Large ranges (up to 10⁹ or 10¹⁸) rule out direct indexing; you’ll need compression, hashing, or math‑based tricks.
  3. Are there any special properties hinted at?

    • The array is sorted → binary search, two‑pointer, or sliding window.
    • Elements are distinct → you can safely use a set without worrying about duplicates.
    • The problem mentions “modulo”, “remainder”, or “divisible” → think prefix sums with hash maps (the classic “subarray sum equals K” pattern).
    • The input is a graph with ≤ 10⁵ edges → adjacency list + BFS/DFS; if it’s a tree, consider DFS with DP.

When I first applied this to that “longest sub‑array sum divisible by K” problem, the clues shouted: N up to 2 × 10⁵ → we need O(N) or O(N log N). Values are large → we can’t bucket by value. The mention of “divisible by K” is a dead‑giveaway for prefix‑sum modulo K. If two prefix sums have the same remainder, the sub‑array between them sums to a multiple of K. So we just need to store the first index we see each remainder in a hash map and compute the distance. Boom – O(N) time, O(K) space (or O(N) if we store all remainders).

The “aha!” moment hit when I realized I’d been over‑thinking the algorithm while the constraints were practically handing me the solution on a silver platter. It felt like Neo dodging bullets in The Matrix – the code was there, I just had to see the trajectory.

Wielding the Power (Code & Examples)

The Struggle – Brute Force Attempt

# O(N^2) – times out for N = 2e5
def longest_subarray_divisible_k_brute(arr, k):
    n = len(arr)
    best = 0
    for i in range(n):
        s = 0
        for j in range(i, n):
            s += arr[j]
            if s % k == 0:
                best = max(best, j - i + 1)
    return best
Enter fullscreen mode Exit fullscreen mode

The Victory – Applying the Framework

def longest_subarray_divisible_k(arr, k):
    """
    Returns the length of the longest subarray whose sum is divisible by k.
    O(N) time, O(min(N, k)) space.
    """
    # remainder -> earliest index where this remainder appeared
    first_occurrence = {0: -1}   # prefix sum 0 before the array starts
    prefix = 0
    best = 0

    for i, num in enumerate(arr):
        prefix = (prefix + num) % k
        # Python's % can be negative, fix it
        if prefix < 0:
            prefix += k

        if prefix in first_occurrence:
            length = i - first_occurrence[prefix]
            if best < length:
                best = length
        else:
            # store the first time we see this remainder
            first_occurrence[prefix] = i

    return best
Enter fullscreen mode Exit fullscreen mode

Why this works:

  • The prefix sum modulo k captures exactly the information we need about divisibility.
  • By remembering the earliest index for each remainder, we guarantee the longest possible stretch when we see the same remainder again.
  • The hash map gives O(1) average look‑ups, keeping the whole algorithm linear.

Common Traps to Avoid

Trap What Happens How to Dodge It
Forgetting to initialise {0: -1} Sub‑arrays that start at index 0 are missed, giving a wrong answer. Always seed the map with remainder 0 at position -1 (or 0 if you prefer 1‑based indexing).
Using the raw prefix sum without modulo Numbers blow up, memory explodes, and you lose the divisibility property. Reduce modulo k at every step; keep the value in [0, k-1].
Over‑looking negative remainders in Python -3 % 5 yields 2, but if you adjust incorrectly you’ll get mismatched keys. Normalise: r = ((prefix % k) + k) % k or simply if r < 0: r += k.

Why This New Power Matters

Adopting this constraint‑first mindset has turned my coding interviews from nail‑biting marathons into quick puzzles. I no longer stare at a blank screen hoping for inspiration; I interrogate the limits, match them to patterns I’ve seen before, and write the solution with confidence.

The payoff is real:

  • Speed: I can sketch a working algorithm in under five minutes for most medium‑difficulty problems.
  • Accuracy: Fewer off‑by‑one errors because I’m not guessing; I’m following a logical deduction chain.
  • Confidence: Walking into a challenge knowing I have a “detective’s toolkit” makes the whole process fun, not stressful.

Give it a try on your next problem. Look at the numbers, ask those three questions, and let the constraints whisper the algorithm to you.

Your Turn – A Mini Quest

Pick any recent LeetCode or Codeforces problem you’ve struggled with. Write down the constraints, run through the three questions, and see which pattern jumps out. If you get stuck, drop a comment with the problem link and your reasoning – let’s solve it together!

Happy hunting, and may your code always be as sharp as a detective’s deductive mind. 🚀

Top comments (0)