Interval DP works on a contiguous range [i, j] of one string or array. Either you shrink the range from its ends (palindromes), or you split it at some point k and combine the two sides (burst balloons, cutting a stick, triangulating a polygon). Both shapes use the same state: the pair of ends.
The idea#
Shrink from both ends. For palindromes, only the two extreme characters matter at each step. If s[i] == s[j], they can be the outer pair of a palindrome, worth 2 plus whatever palindrome fits inside [i+1, j-1]. If they differ, at least one of them isn't used. So drop the left one or the right one and keep the better result.
Split at a point. For "split into parts" problems, try every split point k inside the range and take the best cost(i, k) + cost(k, j) + combine. The trick is choosing a k that makes the two sides independent. Often that means k is the last action to happen in the range, not the first.
Both shapes are correct for the same reason: each range depends only on strictly shorter ranges. Once the two sides are independent, the best answer for the range is the best over every k.
How to spot it#
Look for these signals in the question.
- Palindromes: longest palindromic subsequence, palindromic substrings, minimum cuts.
- "Split into parts", "merge adjacent", "cut", "burst", "triangulate": the cost of a step depends on its neighbors.
nup to a few hundred, since the split version is .
Template#
Here is the shrink shape, used for palindromes. Generating every subsequence is exponential; working from the two ends inward is .
solve(i, j):
if i > j: return 0 # pointers crossed: empty range
if i == j: return 1 # one element
if s[i] == s[j]:
return 2 + solve(i + 1, j - 1) # both ends join
return max(solve(i + 1, j), # drop the left end
solve(i, j - 1)) # drop the right end
memoize solve on (i, j)
answer: solve(0, n - 1)
The split shape#
The second shape is for "split into parts" problems. Try every k strictly inside the range, and choose k so its cost depends only on the range ends, not on what happened inside.
best(i, j): # work strictly between i and j
result = 0 if nothing lies between i and j
for each k from i + 1 to j - 1:
result = best of (result,
best(i, k) + cost of k given i and j + best(k, j))
return result
memoize best on (i, j); padding the ends often helps
If you write either shape bottom-up, fill by increasing range length. Each dp[i][j] needs shorter ranges first.
Pitfalls#
These mistakes cause the most wrong answers.
- Splitting on an action whose neighbors change as the sides shrink. The subproblems are then not independent.
- Missing the
i > jbase case. With an even-length palindrome,i + 1 > j - 1happens and must return 0. - Filling a bottom-up table row by row from the top. Fill by increasing length (or
ifrom the bottom,jfrom left to right). - Using split positions in the order given. The range ends must be in sorted order.
Problems in this pattern#
These problems use this pattern. Solve them before you open the hints below. Start with the first three.
- 516. Longest Palindromic Subsequence
- 647. Palindromic Substrings
- 5. Longest Palindromic Substring
- 1039. Minimum Score Triangulation of Polygon
- 132. Palindrome Partitioning II
- 312. Burst Balloons
- 1547. Minimum Cost to Cut a Stick
Hints#
Open a hint only after you have tried the problem for a while.
Hint: Longest Palindromic Subsequence
This is the shrink template as written. Equal ends add 2; unequal ends drop one side.
Hint: Palindromic Substrings
For contiguous substrings, expand around each of the 2n - 1 centers. That is time and space. The DP form is_pal(i, j) = s[i] == s[j] and is_pal(i+1, j-1) gives the same result.
Hint: Longest Palindromic Substring
Expand around every center, as in Palindromic Substrings, and keep the longest span you find.
Hint: Palindrome Partitioning II
Precompute the is_pal table first. Then cuts(i) is the fewest cuts for s[i:]: 0 if s[i:] is already a palindrome, else 1 + min(cuts(j + 1)) over every j where s[i..j] is a palindrome.
Hint: Burst Balloons
Pad the array with a 1 at each end. In the open range (i, j), pick the balloon k burst last. Its neighbors at that moment are exactly vals[i] and vals[j], so it earns vals[i] * vals[k] * vals[j]. Picking the first balloon fails: its neighbors change as the sides shrink.
Hint: Minimum Cost to Cut a Stick
Sort the cuts, and add 0 and n as ends. The state is a pair of cut positions. The first cut in a piece costs the piece's length.
Hint: Minimum Score Triangulation of Polygon
This is the matrix-chain shape: dp(i, j) = min(dp(i, k) + dp(k, j) + v[i]*v[k]*v[j]).
Key takeaways#
- State = the two ends of a range.
- Palindromes: compare the extremes; equal adds 2, unequal drops one end.
- Split problems: try every
k, and pickkso the two sides are independent ("last to happen"). - Fill tables by increasing range length.