That integral above is my first use of the Data.MemoCombinators library. It memoizes the function, so that the divisors for each input are only computed once (the first time they're requested) and then cached to be reused for future calls.

This saves me some time. For example, if I'm used divisors inside an m -fold loop, the loop would be O(m * n) without memoization, but only O(m + n) with memoization.

Consider again working backwards from a string of length n . Though the last meta-operation may have operated upon a shorter (Append, Copy/Paste) or longer (Delete) string, if were looking at the chain operations that leads most efficiently to our string of length n , than the string that the meta-operation acted upon must be, by definition, more efficient, since we had to spend further ticks after reaching it. So if we know how to efficiently construct strings in less than t ticks, we can use that to efficiently construct strings in exactly t ticks.

This opens us up to Dynamic Programming, which is the real reason I broke out Data.MemoCombinators . Below we use memo2 to memoize a two argument function that calculates the chains required to generate a string of length n in exactly t ticks using the exact working backward method discussed above.