CodeGround
DashboardProblemsInterviewsAssessmentsCourses
CodeGround
DashboardProblemsInterviewsAssessmentsCourses
The Technical Interview Course

0 of 170 lessons complete

  • Recursion with the answers remembered
  • Take it or skip it
  • Reducing a new problem to a solved one
  • When the state is a position in a string
  • Building up to a target
  • When one running value is not enough
  • A table of yes and no
  • Best answer ending here
  • Growing outward from a center
  • Each item once
  • Retrieval: naming the state

One-dimensional Dynamic Programming

130 / 170

One-dimensional Dynamic Programming/Lesson 6 of 11·About 42 min

When one running value is not enough

If a bad value can become good, you have to carry it too.

Key idea

Where a single running value fails

Carrying the best answer ending at each position works when extending a good answer keeps it good. That fails as soon as an operation can flip a bad value into a good one.

Multiplication by a negative is the standard case. The most negative running product, multiplied by another negative, becomes the largest. So the smallest value so far is not useless; it is a candidate.

Why it works

Carry both, and consider starting fresh

Keep two running values ending at the current position: the largest and the smallest. At each step, the new candidates are the element alone, the element times the previous largest, and the element times the previous smallest.

Take the maximum of those three for the new largest and the minimum for the new smallest. The element alone is what allows a run to restart, which matters whenever the running value has become useless.

1# The running high and low from everything up to here.2high, low = 3, -63value = -44 5candidates = (value, value * high, value * low)6print("start fresh, extend the high, extend the low:", candidates)7print("new high:", max(candidates), " new low:", min(candidates))
The new best comes from the old *worst*. Tracking only the high would have missed 24 entirely.

Gotcha

Compute both from the old values

high, low = max(candidates), min(candidates) uses the candidates built from the previous pair. Assigning high first and then computing low from the updated high is wrong and is the usual way this breaks.

Building the candidate tuple first makes the mistake impossible, which is why it is worth the extra line.

Tip

The input that separates the two solutions

Take [-2, 3, -4]. All three multiplied together give 24, and a solution carrying only the largest discards the negative running value at the second step and returns 3.

Any test input without two negatives separated by something cannot tell those two solutions apart. When a problem hinges on sign, make sure your own tests include the case that needs both negatives, because the examples in a prompt frequently do not.

Edge cases

Zero resets everything

A zero makes both running values zero, and the element-alone candidate is what lets the run restart afterward. On [-2, 0, -1] the answer is 0, because every non-empty run containing the zero is zero and both alternatives are negative.

Without the element-alone candidate, a zero would trap both running values at zero forever. That candidate is doing two jobs: restarting after a zero, and restarting after a run that has become worthless.

Key idea

The general lesson

Before settling on a single running value, ask whether any future operation could make a currently-bad value good. If yes, carry both extremes.

The same reasoning applies to running sums with a subtraction available, or to any accumulation where the operation is not monotone. Recognizing non-monotonicity is the trigger.

Predict the output· 5 min

Predict the output: tracking only the best

This carries a single running best, the way the sum version does. Type the two values it prints, separated by a single space.

Python
1def largest_run_product_broken(values):2    best = running = values[0]3    for value in values[1:]:4        running = max(value, running * value)5        best = max(best, running)6    return best7 8print(largest_run_product_broken([2, 3, -2, 4]), largest_run_product_broken([-2, 3, -4]))
Problem· 25 min

Maximum Product Subarray

Read the constraints first and let them tell you what complexity is expected. Derive the approach, implement it, run the tests, and submit when it passes.

PreviousBuilding up to a target
NextA table of yes and no

0 of 3 parts complete