Given a string s containing lowercase English letters.
- Start from any index containing the character 'a' and perform jump operations.
- In each jump operation, move to any index on the right side whose character is the immediate next letter of the current character in the alphabet (i.e., 'a' to 'b', 'b' to 'c', 'c' to 'd', and so on). Continue performing jumps until no further jump is possible.
Find the maximum possible difference between the starting index and the ending index. If it is not possible to choose a starting index, return -1.
Examples :
Input: s = "aaabcb"
Output: 5
Explanation: Start at index 0 ('a'), jump to index 5 ('b'). Difference = 5 - 0 = 5.Input: s = "xynjir"
Output: -1
Explanation: The string does not contain any character 'a'. So, the answer is -1.Input: s = "abcbzzd"
Output: 6
Explanation: Start from index 0 ('a'). Jump to index 1 ('b') because 'b' is the next alphabet character. Jump to index 2 ('c') because 'c' is the next character after 'b'. Jump to index 6 ('d') because 'd' is the next character after 'c'.
Table of Content
[Brute Force Approach] - Using Recursion - O(n à 2n ) Time and O(n) Space
The idea is to recursively explore all possible jump sequences starting from every index containing 'a'. For each next index, try all possible positions on the right containing the next alphabet character and continue the recursion from there.
#include <iostream>
using namespace std;
// Returns farthest index reachable from index i
int farthestReachable(int i, string &s) {
int farthest = i;
// Try all possible jumps
// to the next character
for (int j = i + 1; j < s.size(); j++) {
if (s[j] == s[i] + 1) {
farthest = max(farthest, farthestReachable(j, s));
}
}
return farthest;
}
int maxIndexDifference(string &s) {
int ans = -1;
for (int i = 0; i < s.size(); i++) {
if (s[i] == 'a') {
ans = max(ans, farthestReachable(i, s) - i);
}
}
return ans;
}
int main() {
string s = "aaabcb";
cout << maxIndexDifference(s) << endl;
return 0;
}
class GFG {
// Returns farthest index reachable from index i
static int farthestReachable(int i, String s) {
int farthest = i;
// Try all possible jumps
// to the next character
for (int j = i + 1; j < s.length(); j++) {
if (s.charAt(j) == s.charAt(i) + 1) {
farthest = Math.max(farthest, farthestReachable(j, s));
}
}
return farthest;
}
static int maxIndexDifference(String s) {
int ans = -1;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == 'a') {
ans = Math.max(ans, farthestReachable(i, s) - i);
}
}
return ans;
}
public static void main(String[] args) {
String s = "aaabcb";
System.out.println(maxIndexDifference(s));
}
}
# Returns farthest index reachable from index i
def farthestReachable(i, s):
farthest = i
# Try all possible jumps
# to the next character
for j in range(i + 1, len(s)):
if ord(s[j]) == ord(s[i]) + 1:
farthest = max(farthest, farthestReachable(j, s))
return farthest
def maxIndexDifference(s):
ans = -1
for i in range(len(s)):
if s[i] == 'a':
ans = max(ans, farthestReachable(i, s) - i)
return ans
if __name__ == "__main__":
s = "aaabcb"
print(maxIndexDifference(s))
using System;
class GFG {
// Returns farthest index reachable from index i
static int farthestReachable(int i, string s) {
int farthest = i;
// Try all possible jumps
// to the next character
for (int j = i + 1; j < s.Length; j++) {
if (s[j] == s[i] + 1) {
farthest = Math.Max(farthest, farthestReachable(j, s));
}
}
return farthest;
}
static int maxIndexDifference(string s) {
int ans = -1;
for (int i = 0; i < s.Length; i++) {
if (s[i] == 'a') {
ans = Math.Max(ans, farthestReachable(i, s) - i);
}
}
return ans;
}
static void Main(string[] args) {
string s = "aaabcb";
Console.WriteLine(maxIndexDifference(s));
}
}
// Returns farthest index reachable from index i
function farthestReachable(i, s) {
let farthest = i;
// Try all possible jumps
// to the next character
for (let j = i + 1; j < s.length; j++) {
if (s.charCodeAt(j) === s.charCodeAt(i) + 1) {
farthest = Math.max(farthest, farthestReachable(j, s));
}
}
return farthest;
}
function maxIndexDifference(s) {
let ans = -1;
for (let i = 0; i < s.length; i++) {
if (s[i] === 'a') {
ans = Math.max(ans, farthestReachable(i, s) - i);
}
}
return ans;
}
// Driver code
const s = "aaabcb";
console.log(maxIndexDifference(s));
Output
5
[Better Approach] - Using Recursion + Memoization - O(n2) Time and O(n) Space
The idea is to store the farthest reachable index from every position. For each index, recursively explore all possible jumps to the next alphabet character and store the result. This avoids recalculating the same index multiple times, but each state still scans the remaining part of the string to find possible jumps.
#include <iostream>
#include <vector>
using namespace std;
// Returns farthest index reachable from index i
int farthestReachable(int i, string &s, vector<int> &dp) {
if (dp[i] != -1) return dp[i];
int farthest = i;
// Try all possible jumps
// to the next character
for (int j = i + 1; j < (int)s.size(); j++) {
if (s[j] == s[i] + 1)
farthest = max(farthest, farthestReachable(j, s, dp));
}
return dp[i] = farthest;
}
int maxIndexDifference(string &s) {
int n = s.size();
vector<int> dp(n, -1);
int ans = -1;
// Try every 'a' as a starting point
for (int i = 0; i < n; i++) {
if (s[i] == 'a')
ans = max(ans, farthestReachable(i, s, dp) - i);
}
return ans;
}
int main() {
string s = "aaabcb";
cout << maxIndexDifference(s) << endl;
return 0;
}
import java.util.Arrays;
public class GFG {
// Returns farthest index reachable from index i
static int farthestReachable(int i, String s, int[] dp) {
if (dp[i] != -1) return dp[i];
int farthest = i;
// Try all possible jumps
// to the next character
for (int j = i + 1; j < s.length(); j++) {
if (s.charAt(j) == s.charAt(i) + 1)
farthest = Math.max(farthest, farthestReachable(j, s, dp));
}
return dp[i] = farthest;
}
static int maxIndexDifference(String s) {
int n = s.length();
int[] dp = new int[n];
Arrays.fill(dp, -1);
int ans = -1;
// Try every 'a' as a starting point
for (int i = 0; i < n; i++) {
if (s.charAt(i) == 'a')
ans = Math.max(ans, farthestReachable(i, s, dp) - i);
}
return ans;
}
public static void main(String[] args) {
String s = "aaabcb";
System.out.println(maxIndexDifference(s));
}
}
# Returns farthest index reachable from index i
def farthestReachable(i, s, dp):
if dp[i] != -1:
return dp[i]
farthest = i
# Try all possible jumps
# to the next character
for j in range(i + 1, len(s)):
if ord(s[j]) == ord(s[i]) + 1:
farthest = max(farthest, farthestReachable(j, s, dp))
dp[i] = farthest
return dp[i]
def maxIndexDifference(s):
n = len(s)
dp = [-1] * n
ans = -1
# Try every 'a' as a starting point
for i in range(n):
if s[i] == 'a':
ans = max(ans, farthestReachable(i, s, dp) - i)
return ans
if __name__ == "__main__":
s = "aaabcb"
print(maxIndexDifference(s))
using System;
class GFG {
// Returns farthest index reachable from index i
static int farthestReachable(int i, string s, int[] dp) {
if (dp[i] != -1) return dp[i];
int farthest = i;
// Try all possible jumps
// to the next character
for (int j = i + 1; j < s.Length; j++) {
if (s[j] == s[i] + 1)
farthest = Math.Max(farthest, farthestReachable(j, s, dp));
}
return dp[i] = farthest;
}
static int maxIndexDifference(string s) {
int n = s.Length;
int[] dp = new int[n];
Array.Fill(dp, -1);
int ans = -1;
// Try every 'a' as a starting point
for (int i = 0; i < n; i++) {
if (s[i] == 'a')
ans = Math.Max(ans, farthestReachable(i, s, dp) - i);
}
return ans;
}
static void Main(string[] args) {
string s = "aaabcb";
Console.WriteLine(maxIndexDifference(s));
}
}
// Returns farthest index reachable from index i
function farthestReachable(i, s, dp) {
if (dp[i] !== -1) return dp[i];
let farthest = i;
// Try all possible jumps
// to the next character
for (let j = i + 1; j < s.length; j++) {
if (s.charCodeAt(j) === s.charCodeAt(i) + 1)
farthest = Math.max(farthest, farthestReachable(j, s, dp));
}
return dp[i] = farthest;
}
function maxIndexDifference(s) {
const n = s.length;
const dp = new Array(n).fill(-1);
let ans = -1;
// Try every 'a' as a starting point
for (let i = 0; i < n; i++) {
if (s[i] === 'a')
ans = Math.max(ans, farthestReachable(i, s, dp) - i);
}
return ans;
}
// Driver code
const s = "aaabcb";
console.log(maxIndexDifference(s));
Output
5
[Expected Approach] - Using Dynamic Programming (Right-to-Left Traversal) - O(n) Time and O(1) Space
The idea is to process the string from right to left because every jump is only allowed towards the right side. While traversing, maintain the farthest reachable index for each character. For the current character, if the next alphabet character has already been processed, we can directly use its farthest reachable index to determine the farthest position reachable from the current index. This avoids checking all possible jumps and allows each character to be processed in constant time.
#include <iostream>
#include <vector>
using namespace std;
int maxIndexDifference(string &s) {
int n = s.size();
// best[i] stores the farthest
// reachable index for character ('a' + i)
vector<int> best(26, -1);
// Remains -1 if no valid
// starting index ('a') exists
int ans = -1;
// Process from right to left
// to consider only jumps to the right
for (int i = n - 1; i >= 0; i--) {
int farthest = i;
// Jump to the next alphabet
// character if it is reachable
if (s[i] != 'z' && best[s[i] - 'a' + 1] != -1) {
farthest = best[s[i] - 'a' + 1];
}
best[s[i] - 'a'] = max(best[s[i] - 'a'], farthest);
// Only 'a' can be a starting point
if (s[i] == 'a') {
ans = max(ans, farthest - i);
}
}
return ans;
}
int main() {
string s = "aaabcb";
cout << maxIndexDifference(s) << endl;
return 0;
}
public class GFG {
static int maxIndexDifference(String s) {
int n = s.length();
// best[i] stores the farthest
// reachable index for character ('a' + i)
int[] best = new int[26];
java.util.Arrays.fill(best, -1);
// Remains -1 if no valid
// starting index ('a') exists
int ans = -1;
// Process from right to left
// to consider only jumps to the right
for (int i = n - 1; i >= 0; i--) {
int farthest = i;
// Jump to the next alphabet
// character if it is reachable
if (s.charAt(i) != 'z' && best[s.charAt(i) - 'a' + 1] != -1) {
farthest = best[s.charAt(i) - 'a' + 1];
}
best[s.charAt(i) - 'a'] = Math.max(best[s.charAt(i) - 'a'], farthest);
// Only 'a' can be a starting point
if (s.charAt(i) == 'a') {
ans = Math.max(ans, farthest - i);
}
}
return ans;
}
public static void main(String[] args) {
String s = "aaabcb";
System.out.println(maxIndexDifference(s));
}
}
def maxIndexDifference(s):
n = len(s)
# best[i] stores the farthest
# reachable index for character ('a' + i)
best = [-1] * 26
# Remains -1 if no valid
# starting index ('a') exists
ans = -1
# Process from right to left to
# consider only jumps to the right
for i in range(n - 1, -1, -1):
farthest = i
# Jump to the next alphabet
# character if it is reachable
if s[i] != 'z' and best[ord(s[i]) - ord('a') + 1] != -1:
farthest = best[ord(s[i]) - ord('a') + 1]
best[ord(s[i]) - ord('a')] = max(best[ord(s[i]) - ord('a')], farthest)
# Only 'a' can be a starting point
if s[i] == 'a':
ans = max(ans, farthest - i)
return ans
if __name__ == "__main__":
s = "aaabcb"
print(maxIndexDifference(s))
using System;
class GFG {
static int MaxIndexDifference(string s) {
int n = s.Length;
// best[i] stores the farthest
// reachable index for character ('a' + i)
int[] best = new int[26];
Array.Fill(best, -1);
// Remains -1 if no valid
// starting index ('a') exists
int ans = -1;
// Process from right to left to
// consider only jumps to the right
for (int i = n - 1; i >= 0; i--) {
int farthest = i;
// Jump to the next alphabet
// character if it is reachable
if (s[i] != 'z' && best[s[i] - 'a' + 1] != -1) {
farthest = best[s[i] - 'a' + 1];
}
best[s[i] - 'a'] = Math.Max(best[s[i] - 'a'], farthest);
// Only 'a' can be a starting point
if (s[i] == 'a') {
ans = Math.Max(ans, farthest - i);
}
}
return ans;
}
static void Main(string[] args) {
string s = "aaabcb";
Console.WriteLine(MaxIndexDifference(s));
}
}
function maxIndexDifference(s) {
const n = s.length;
// best[i] stores the farthest
// reachable index for character ('a' + i)
const best = new Array(26).fill(-1);
// Remains -1 if no valid
// starting index ('a') exists
let ans = -1;
// Process from right to left to
// consider only jumps to the right
for (let i = n - 1; i >= 0; i--) {
let farthest = i;
// Jump to the next alphabet
// character if it is reachable
if (s[i] !== 'z' && best[s.charCodeAt(i) - 97 + 1] !== -1) {
farthest = best[s.charCodeAt(i) - 97 + 1];
}
best[s.charCodeAt(i) - 97] = Math.max(best[s.charCodeAt(i) - 97], farthest);
// Only 'a' can be a starting point
if (s[i] === 'a') {
ans = Math.max(ans, farthest - i);
}
}
return ans;
}
// Driver code
const s = "aaabcb";
console.log(maxIndexDifference(s));
Output
5