Furthest Point From Origin
EasyPrompt
You are given a string moves of length n consisting only of characters 'L', 'R', and '_'. The string represents your movement on a number line starting from the origin 0.
In the ith move, you can choose one of the following directions:
- move to the left if
moves[i] = 'L'ormoves[i] = '_' - move to the right if
moves[i] = 'R'ormoves[i] = '_'
Return the distance from the origin of the furthest point you can get to after n moves.
Example 1:
Input: moves = "L_RL__R"
Output: 3
Explanation: The furthest point we can reach from the origin 0 is point -3 through the following sequence of moves "LLRLLLR".Example 2:
Input: moves = "_R__LL_"
Output: 5
Explanation: The furthest point we can reach from the origin 0 is point -5 through the following sequence of moves "LRLLLLL".Example 3:
Input: moves = "_______"
Output: 7
Explanation: The furthest point we can reach from the origin 0 is point 7 through the following sequence of moves "RRRRRRR".
Constraints:
1 <= moves.length == n <= 50movesconsists only of characters'L','R'and'_'.
Approaches
3 approaches with complexity analysis and trade-offs.
This approach explores every possible outcome by treating each underscore _ as a decision point. For each _, we can either move left or move right. A recursive function can be used to explore all these paths. The function would take the current index in the moves string and the current position on the number line as parameters. When an _ is encountered, the function makes two recursive calls: one for moving left and one for moving right. The final result is the maximum absolute position found among all possible paths.
Algorithm
- Define a recursive function
findMaxDistance(moves, index, currentPosition). - If
indexequals the length ofmoves, returnabs(currentPosition). - Get the character
moveat the currentindex. - If
moveis 'L', recursively callfindMaxDistance(moves, index + 1, currentPosition - 1). - If
moveis 'R', recursively callfindMaxDistance(moves, index + 1, currentPosition + 1). - If
moveis '_', make two recursive calls: one for moving left (currentPosition - 1) and one for moving right (currentPosition + 1). Return the maximum of the results from these two calls. - Start the process by calling
findMaxDistance(moves, 0, 0).
Walkthrough
The core idea is to build a recursive function, say findMaxDistance(index, currentPosition).
- Base Case: When the
indexreaches the end of themovesstring, it means we have processed all moves. We return the absolute value of thecurrentPosition, which represents the distance from the origin for one complete path. - Recursive Step:
- For a given
index, we look atmoves.charAt(index). - If it's 'L', we decrement the position and recurse:
findMaxDistance(index + 1, currentPosition - 1). - If it's 'R', we increment the position and recurse:
findMaxDistance(index + 1, currentPosition + 1). - If it's '_', we have a choice. We must explore both possibilities to find the maximum possible distance.
- Move Left:
distanceLeft = findMaxDistance(index + 1, currentPosition - 1). - Move Right:
distanceRight = findMaxDistance(index + 1, currentPosition + 1). - We return the maximum of the two outcomes:
Math.max(distanceLeft, distanceRight).
- Move Left:
- For a given
- The initial call to the function would be
findMaxDistance(0, 0).
class Solution { public int furthestDistanceFromOrigin(String moves) { return findMaxDistance(moves, 0, 0); } private int findMaxDistance(String moves, int index, int currentPosition) { // Base case: we've processed all moves if (index == moves.length()) { return Math.abs(currentPosition); } char move = moves.charAt(index); if (move == 'L') { return findMaxDistance(moves, index + 1, currentPosition - 1); } else if (move == 'R') { return findMaxDistance(moves, index + 1, currentPosition + 1); } else { // move == '_' // Explore both possibilities and return the one that leads to a further distance int distIfLeft = findMaxDistance(moves, index + 1, currentPosition - 1); int distIfRight = findMaxDistance(moves, index + 1, currentPosition + 1); return Math.max(distIfLeft, distIfRight); } }}Complexity
Time
O(2^k), where `k` is the number of `_` characters in the `moves` string. In the worst case, the entire string consists of `_`, so `k=n`, leading to `O(2^n)`. This is because for each `_`, the number of computation paths doubles.
Space
O(n), where n is the length of the `moves` string. This is due to the maximum depth of the recursion stack.
Trade-offs
Pros
Simple to understand and implement the logic of exploring all possibilities.
Correctly solves the problem for small inputs.
Cons
Highly inefficient. The exponential time complexity makes it infeasible for larger inputs (e.g.,
n=50). It will result in a "Time Limit Exceeded" error on most platforms.
The brute-force recursive approach suffers from re-computing the same subproblems multiple times. For example, findMaxDistance(index, position) might be called with the same index and position through different paths. We can optimize this by storing the results of these subproblems in a cache or a memoization table. This technique is known as Dynamic Programming.
Algorithm
- Create a 2D memoization table
memoof size(n+1) x (2n+1)to store results of subproblems, initialized to a null/sentinel value. - Define a recursive function
findMaxDistance(moves, index, position, memo). - The position can range from
-nton. Use an offset ofnto map it to a valid array indexposition + n. - Base Case: If
index == moves.length(), returnabs(position). - Memoization Check: If
memo[index][position + n]is already computed, return it. - Recursive Step: Compute the result based on
moves.charAt(index)as in the brute-force approach. - Store Result: Store the computed result in
memo[index][position + n]before returning. - The initial call is
findMaxDistance(moves, 0, 0, memo).
Walkthrough
The state of our subproblem can be defined by (index, currentPosition). We want to find the maximum distance from the origin we can achieve starting from index with a currentPosition.
We use a 2D array, memo, to store the results. memo[index][position] will store the result for findMaxDistance(index, position). The position can be negative, so we need to offset it to use it as an array index. Since the position can range from -n to n, we can use an offset of n. So, memo[index][position + n] will be used.
The recursive function is modified to check the memoization table before computing.
class Solution { public int furthestDistanceFromOrigin(String moves) { int n = moves.length(); // memo[index][position + n] Integer[][] memo = new Integer[n + 1][2 * n + 1]; return findMaxDistance(moves, 0, 0, memo); } private int findMaxDistance(String moves, int index, int currentPosition, Integer[][] memo) { int n = moves.length(); // Base case if (index == n) { return Math.abs(currentPosition); } // Check memoization table (position is offset by n) if (memo[index][currentPosition + n] != null) { return memo[index][currentPosition + n]; } char move = moves.charAt(index); int result; if (move == 'L') { result = findMaxDistance(moves, index + 1, currentPosition - 1, memo); } else if (move == 'R') { result = findMaxDistance(moves, index + 1, currentPosition + 1, memo); } else { // move == '_' int distIfLeft = findMaxDistance(moves, index + 1, currentPosition - 1, memo); int distIfRight = findMaxDistance(moves, index + 1, currentPosition + 1, memo); result = Math.max(distIfLeft, distIfRight); } // Store result in memo and return memo[index][currentPosition + n] = result; return result; }}This ensures that each subproblem (index, position) is solved only once.
Complexity
Time
O(n^2), where `n` is the length of `moves`. The number of states is `n * (2n+1)`, which is `O(n^2)`. Each state is computed once.
Space
O(n^2) for the memoization table. The recursion stack depth adds `O(n)`, but it's dominated by the table size.
Trade-offs
Pros
Much more efficient than the brute-force approach.
Guaranteed to pass within time limits for the given constraints (
n <= 50).
Cons
Uses significant space (
O(n^2)).More complex to implement than the optimal single-pass solution.
A closer look at the problem reveals a greedy strategy. To maximize the distance from the origin, we want to go as far as possible in one direction (either all the way to the right or all the way to the left). Any _ move can be used to contribute to this one-directional push. The fixed 'L' and 'R' moves establish a base displacement. All the flexible _ moves should then be used to augment this displacement in the direction that leads further from the origin.
Algorithm
- Initialize three counters:
countL = 0,countR = 0,countUnderscore = 0. - Iterate through the
movesstring once. - For each character, increment the corresponding counter.
- After the loop, calculate the net displacement from fixed moves:
baseDisplacement = countR - countL. - The maximum distance is the absolute value of the base displacement plus the number of flexible moves:
Math.abs(baseDisplacement) + countUnderscore. - Return this value.
Walkthrough
The key insight is that to achieve the maximum distance, all _ characters must be resolved to the same direction: either all become 'L' or all become 'R'.
- Let's count the number of 'L', 'R', and '_' characters in the string. Let these be
countL,countR, andcountUnderscore. - The net displacement from the fixed moves ('L' and 'R') is
countR - countL. - Now, we have
countUnderscoremoves that we can choose.- Scenario 1: Go as far right as possible. We treat every
_as an 'R'. The final position would be(countR - countL) + countUnderscore. - Scenario 2: Go as far left as possible. We treat every
_as an 'L'. The final position would be(countR - countL) - countUnderscore.
- Scenario 1: Go as far right as possible. We treat every
- The furthest distance from the origin is the maximum of the absolute values of these two potential final positions.
max_distance = Math.max(Math.abs((countR - countL) + countUnderscore), Math.abs((countR - countL) - countUnderscore))- This can be simplified. Let
base = countR - countLandflex = countUnderscore. We needmax(abs(base + flex), abs(base - flex)). This expression is mathematically equivalent toabs(base) + flex. - So, the final answer is simply
Math.abs(countR - countL) + countUnderscore. - The algorithm is to iterate through the string once to get the counts, and then apply this formula.
class Solution { public int furthestDistanceFromOrigin(String moves) { int countL = 0; int countR = 0; int countUnderscore = 0; for (char move : moves.toCharArray()) { if (move == 'L') { countL++; } else if (move == 'R') { countR++; } else { countUnderscore++; } } // The furthest we can go right is (countR - countL) + countUnderscore // The furthest we can go left is (countR - countL) - countUnderscore // The distance is the max of the absolute values of these two positions. // This simplifies to abs(countR - countL) + countUnderscore. int baseDisplacement = countR - countL; return Math.abs(baseDisplacement) + countUnderscore; }}Complexity
Time
O(n), where `n` is the length of the `moves` string. We perform a single pass over the string.
Space
O(1). We only use a few variables to store the counts, regardless of the input size.
Trade-offs
Pros
Extremely efficient in both time and space.
Simple and elegant solution.
Cons
Requires a key insight that might not be immediately obvious. The greedy choice needs to be justified.
Solutions
Solution
class Solution {public int furthestDistanceFromOrigin(String moves) { return Math.abs(count(moves, 'L') - count(moves, 'R')) + count(moves, '_'); }private int count(String s, char c) { int cnt = 0; for (int i = 0; i < s.length(); ++i) { if (s.charAt(i) == c) { ++cnt; } } return cnt; }}class Solution {public: int furthestDistanceFromOrigin(string moves) { auto cnt = [&](char c) { return count(moves.begin(), moves.end(), c); }; return abs(cnt('L') - cnt('R')) + cnt('_'); }};class Solution: def furthestDistanceFromOrigin(self, moves: str) -> int: return abs( moves . count("L") - moves . count("R")) + moves . count("_")Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.