Dynamic programming Code Wars twice linear algorithm times out
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
The Codewars twice-linear kata times out when the implementation generates too many candidates and keeps re-sorting them. The sequence has enough structure that you can build it in order with two moving indices and no repeated global cleanup. Once that shift is made, the solution becomes both fast and easy to reason about.
Why Naive Solutions Slow Down
The sequence starts with u[0] = 1. For every value x already in the sequence, two new candidates appear:
- '
2x + 1' - '
3x + 1'
The slow approach is to keep throwing those candidates into a set or list, sorting repeatedly, and deduplicating over and over. That works for small tests, but the repeated sorting and structure churn dominate the runtime as n grows.
Even if each individual step seems reasonable, the total work becomes much larger than necessary because the solution keeps reorganizing data it already knows is mostly ordered.
The Key Observation: Two Sorted Streams
If the sequence u itself is increasing, then the generated values 2 * u[i] + 1 and 3 * u[j] + 1 also appear in increasing order as i and j move forward. That means you do not need a global sort. You only need to merge two already ordered streams.
This is the same idea used in many efficient sequence-generation problems: generate the next candidate from each source, pick the smaller one, append it, and advance the relevant pointer.
This loop grows the sequence directly in sorted order. There is no heap, no repeated sort, and no expensive full deduplication pass.
Why Advancing Both Pointers Matters
Some values can be generated from both streams. If you only advance one pointer when the candidates are equal, the duplicate value will appear again later.
That is why the solution checks both conditions separately:
This is the detail that often separates a correct fast solution from a nearly correct one that either duplicates values or drifts off the expected sequence.
Validate Correctness Before Benchmarking
Performance only matters after the sequence is correct. A small known-prefix test is usually enough to catch off-by-one errors and duplicate-handling mistakes.
Once those checks succeed, you can benchmark a larger index to confirm the timeout problem is actually gone.
This is the right order of work: correctness first, then performance.
Dynamic Programming Is Not the Main Idea Here
People often describe this solution as dynamic programming, but the more helpful mental model is ordered generation with merging pointers. The algorithm stores previous sequence values, but the real win comes from exploiting the monotonic structure of the candidate streams.
That distinction matters because it keeps you focused on the right optimization. If you think "DP," you may reach for memoization without fixing the real bottleneck. If you think "merge two increasing sources," the linear solution becomes obvious.
The Same Structure Works in Other Languages
The approach is not Python-specific. Here is the same idea in JavaScript:
That makes it clear the speedup comes from the algorithm, not from a language trick or a library choice.
Common Pitfalls
The most common mistake is repeatedly sorting candidate collections inside the loop. Another is using a set-heavy solution that looks clean but adds too much overhead. People also often forget to advance both pointers on equal values, or they benchmark only tiny inputs and conclude the slow approach is good enough.
Summary
- The twice-linear sequence can be generated by merging two increasing candidate streams.
- Repeated sorting and deduplication are the usual cause of timeouts.
- A two-pointer solution builds values directly in order.
- Advancing both pointers on equal candidates is essential.
- Validate the prefix first, then benchmark large inputs to confirm the fix.
Related reading
- Dynamic Programming Coin Change Problems
- Dynamic Programming Sum-of-products
- Dynamic Programming Why the need for optimal sub structure
- Dynamically add new queues, bindings and exchanges as beans
- Dynamic quantization in Pytorch starts random training after quantization
- Dynamically updating shortest paths
- Dynamically changing the instanceindex with spring cloud stream kafka
- e-commerce Algorithm for calculating discounts

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.