Problem summary
You start at position 0 with speed 1. You can issue two instructions:
- A (accelerate): position += speed; speed *= 2
- R (reverse): if speed > 0 then speed = -1 else speed = 1
Given a target position (positive integer), return the minimum length of an instruction sequence that reaches exactly that target.
Examples
Input: target = 3
Output: 2
Explanation: AA (after two A: position 3)
Input: target = 6
Output: 5
One optimal sequence: AAARA — after AAA position is 7 (overshoot), then R (reverse) and A to move back to 6.
Constraints
target is a positive integer (up to around 10^4 or more in general). The expected solution is not brute force; known optimal approaches use BFS or dynamic programming with recursion + memoization.
Intuition and approach in simple English
The race-car state space (position, speed) can be explored with BFS, but that can be heavy if not bounded carefully. A more elegant and faster approach uses recursion + memoization based on the binary nature of accelerating:
- After
nconsecutiveAinstructions (i.e.,Arepeatedntimes), the car reaches position2^n - 1and speed2^n. - For a given
target, find the smallestnsuch that2^n - 1 >= target. There are two main possibilities:
2^n - 1 == target
If this holds, the answer isn(justArepeatedntimes).2^n - 1 > target(we overshoot if we donAs)
You have two kinds of strategies:- Overshoot and come back: do
nAs to reach2^n - 1, thenR(1 op) to reverse, then solve the remaining distance(2^n - 1) - targetrecursively. Total operations:n + 1 + dp((2^n - 1) - target). - Stop short, reverse, backtrack some, reverse again, then go forward to finish: for some
mwith0 <= m < n:- Do
n-1As to reach2^{n-1} - 1 R(reverse), then domAs (moving backwards), thenRagain to face forward, then solve the remaining forward distance recursively.- After
n-1As position =2^{n-1} - 1. After reversing and doingmAs (with negative speed), position becomes2^{n-1} - 1 - (2^m - 1). - Remaining =
target - (2^{n-1} - 1 - (2^m - 1)) = target - (2^{n-1} - 1) + (2^m - 1). - Total ops =
(n-1) + 1 + m + 1 + dp(remaining) = n + m + 1 + dp(remaining).
- Do
- Overshoot and come back: do
Take the minimum over the overshoot strategy and all these stop-short strategies. Use memoization to avoid recomputation.
This recursion captures the optimal structure because the accelerate instruction grows position exponentially and optimal sequences often use these full-accelerate chunks.
Algorithm outline (top-down DP with memo)
- If target == 0, return 0.
- Compute
n = ceil(log2(target + 1)). - If
2^n - 1 == target, returnn. - Initialize answer as overshoot case:
ans = n + 1 + dp((2^n - 1) - target). - For
mfrom0ton-1:- remaining =
target - (2^{n-1} - 1) + (2^m - 1) - candidate =
n + m + 1 + dp(remaining) ans = min(ans, candidate)
- remaining =
- Memoize and return
ans.
Time complexity: Each recursive dp(target) will loop up to n times where n ~ log(target). With memoization, the number of distinct targets seen is limited; overall complexity is roughly exponential in log(target) (polynomial in log target), usually very fast for constraints up to typical LeetCode targets.
Java implementation (top-down memoization)
import java.util.HashMap;
import java.util.Map;
public class Solution {
private Map<Integer, Integer> memo = new HashMap<>();
public int racecar(int target) {
return dp(target);
}
private int dp(int target) {
if (target == 0) return 0;
if (memo.containsKey(target)) return memo.get(target);
// find smallest n such that 2^n - 1 >= target
int n = 1;
while ((1 << n) - 1 < target) {
n++;
}
// if exact match: n A's
if ((1 << n) - 1 == target) {
memo.put(target, n);
return n;
}
// option 1: overshoot with n A's, then R, then solve remainder
int overshootDistance = (1 << n) - 1 - target;
int best = n + 1 + dp(overshootDistance);
// option 2: try stopping earlier, reversing, make some moves, reverse again, then finish
// try m = 0..n-1 where we do (n-1) A's, R, m A's, R, then dp(remaining)
for (int m = 0; m < n; m++) {
int forwardUntil = (1 << (n - 1)) - 1; // position after n-1 As
int backDistance = (1 << m) - 1; // distance covered after m As (in opposite direction)
int remaining = target - (forwardUntil - backDistance);
// ops: (n-1) As + 1 R + m As + 1 R + dp(remaining)
int candidate = (n - 1) + 1 + m + 1 + dp(remaining);
if (candidate < best) best = candidate;
}
memo.put(target, best);
return best;
}
// For quick manual testing
public static void main(String[] args) {
Solution s = new Solution();
System.out.println(s.racecar(3)); // expected 2
System.out.println(s.racecar(6)); // expected 5
System.out.println(s.racecar(1)); // expected 1
System.out.println(s.racecar(4)); // common test
}
}
Notes about the code
1 << ncomputes2^n(works whilenis small enough; for typical constraints this is safe). Iftargetmay be very large (beyond 2^30), uselongshifts or Math.pow.- We use a
HashMap<Integer, Integer>for memoization. This ensures repeated subproblems are answered in O(1). - The
for (m = 0; m < n; m++)tries the strategy of stopping short and making a controlled backtrack — it is necessary to find some optimal sequences.
Dry run (two examples)
Example 1: target = 3
- Find
nsuch that2^n - 1 >= 3.n = 2gives2^2 - 1 = 3, exact match. - Return
n = 2. Sequence:AA.
Example 2: target = 6
n = 3because2^3 - 1 = 7 >= 6and7 != 6.- Overshoot option: do
AAA(3 ops) reach 7, thenR(1 op), remaining =7 - 6 = 1. So cost =3 + 1 + dp(1).dp(1)= 1 (singleA). Total = 5. - Try stopping-short options (m from 0 to 2):
- m = 0: forwardUntil =
2^(3-1)-1 = 3; backDistance =0; remaining =6 - (3 - 0) = 3; cost =(3-1)+1+0+1 + dp(3) = 2+1+0+1 + 2 = 6 - m = 1: forwardUntil = 3; backDistance = 1; remaining =
6 - (3 - 1) = 4; cost =2+1+1+1 + dp(4) = 5 + dp(4)(dp(4) >= 2 so candidate >= 7) - m = 2: forwardUntil = 3; backDistance = 3; remaining =
6 - (3 - 3) = 6; cost =2+1+2+1 + dp(6) = 6 + dp(6)(recursive)
- m = 0: forwardUntil =
- The overshoot path gives best = 5, which is optimal. One concrete instruction sequence is
A A A R A-> positions 1,3,7, then reverse and step to 6.
Complexity and final notes
- Each DP call loops up to
n ≈ log2(target)times, and memoization reduces repeated work. In practice the approach runs quickly for typical target sizes. - Alternative approach: BFS on (position, speed) states with pruning. BFS is intuitive (shortest sequence) but you must bound position and avoid infinite exploration; DP approach leverages structure and is usually faster.
- Edge cases: small targets (1,2,3) are handled quickly by the
2^n - 1checks; memo avoids recomputation for sub-targets.
