Problem Description
Given a string caption of length n, you want to transform it into a “good” caption.
A “good” caption is defined as one in which every letter appears only in contiguous groups, and each such group has at least 3 occurrences.
You may change any character at index i, but only by “shifting” it one step in the alphabet (either to the immediately previous letter – if it isn’t 'a' – or to the immediately next letter – if it isn’t 'z'). Repeating operations allows you to obtain any target letter at the cost equal to the absolute difference in their alphabetical order.
Your goal is to pick the operations so that the total cost (the sum over all indices of the number of single‐step operations performed) is minimized. If there are several optimal “good” captions (i.e. with minimum cost), you must return the lexicographically smallest one. If it is impossible to obtain a good caption using any number of operations, return the empty string.
Key Insights
- The cost to change a letter c to some target letter L is |c - L| since only adjacent moves are allowed.
- The final caption must be piecewise constant with each constant (or “block”) having length at least 3.
- A natural strategy is to decide the final letter for every position while “growing” valid blocks. For the very first two positions of any block the decision is forced: you must use the same letter chosen for the block, otherwise you’d end the block too early.
- Once you have a block of length ≥ 3 (a “complete” block), you have two choices at the next index: either extend the current block with the same letter or start a new block by choosing a different letter (starting the new block with count = 1).
- To combine cost minimization and lexicographical tie‐breaking, dynamic programming with a state that combines the current index, the letter chosen at that position, and the “run length” (with a special flag for counts of 3 or more) is used.
- Pre‐computing the “cost to change” each letter is trivial (simply the absolute difference between character codes) so each transition is O(1). The challenge is to design the DP so that only valid transitions (i.e. forced extension for incomplete groups and both “continue” or “break” options when the block is already valid) are allowed.
Space and Time Complexity
Time Complexity: O(n × 26 × 25)
- n positions, at most 26 possible current letters and, when a block is complete, up to 25 choices to start a new block.
Space Complexity: O(n × 26 × 3)
- We maintain DP state for each index, for each letter and for run lengths (1, 2, and “complete” which we denote by 3).
Solution
We solve the problem by processing the string one character at a time and maintaining a DP state that records the “best” way (in terms of total cost and lexicographical order) to form a valid prefix that ends at that position with a given letter and run count.
The state is defined as dp[i][letter][r] where:
• i: number of characters processed (i from 1 to n; note we use 1-indexing for dp where dp[i] is after having set characters 0…i–1).
• letter: the letter chosen for the current block at position i–1.
• r: the length of the current block. For simplicity we record exact counts for 1 or 2; once the block reaches length 3, we mark that state as “complete” (represented as 3) even if the actual run is larger than 3.
For the first character, you may choose any letter L (cost = |caption[0] - L|) and the block count becomes 1.
For each subsequent character at index i, if the current state is incomplete (r < 3), you are forced to continue with the same letter (and add cost = |caption[i] - L| and increment the run count) until r reaches 3. Once a block is “complete” (r = 3), you have two options:
1. Continue the block with the same letter L (state remains “complete”, r remains 3).
2. End the current block and start a new block with any letter K (K must be different from L), setting the new block’s count to 1 (cost = |caption[i] - K|).
When computing transitions, if two ways yield the same cost, the lexicographically smaller partial caption is chosen.
Finally, a solution is acceptable only if the final state's block is “complete” (r = 3). We then reconstruct the answer using predecessor pointers stored during the DP transitions.
The algorithm uses a tabulation that runs in O(n) time with constant work per state transition and uses auxiliary tables for predecessor (to reassemble the solution) and cost/lex order information.
Code Solutions
Below are code solutions in Python, JavaScript, C++, and Java. Each solution uses detailed, line‐by‐line comments.