Before attempting this problem, you should be comfortable with:
Subsequence Concept - Understanding what makes one string a subsequence of another
Two Pointers Technique - Using pointers to traverse and compare strings efficiently
Binary Search - Finding elements in sorted arrays for optimized character lookups
Precomputation / Dynamic Programming - Building lookup tables to enable O(1) queries for next character positions
1. Concatenate until Subsequence
Intuition
The most straightforward approach is to literally concatenate copies of source until target becomes a subsequence of the concatenated string. We first check if all characters in target exist in source; if not, it is impossible. Then we keep appending source to itself and check after each concatenation whether target is now a subsequence. The number of concatenations gives us our answer.
Algorithm
Build a set of characters present in source.
For each character in target, check if it exists in source. If any character is missing, return -1.
Start with concatenatedSource = source and count = 1.
While target is not a subsequence of concatenatedSource:
classSolution:defshortestWay(self, source:str, target:str)->int:# To check if to_check is subsequence of in_stringdefis_subsequence(to_check, in_string):
i = j =0while i <len(to_check)and j <len(in_string):if to_check[i]== in_string[j]:
i +=1
j +=1return i ==len(to_check)# Set of all characters of the source. We could use a boolean array as well.
source_chars =set(source)# Check if all characters of the target are present in the source# If any character is not present, return -1for char in target:if char notin source_chars:return-1# Concatenate source until the target is a subsequence# of the concatenated string
concatenated_source = source
count =1whilenot is_subsequence(target, concatenated_source):
concatenated_source += source
count +=1# Number of concatenations donereturn count
classSolution{publicintshortestWay(String source,String target){// Boolean array to mark all characters of sourceboolean[] sourceChars =newboolean[26];for(char c : source.toCharArray()){
sourceChars[c -'a']=true;}// Check if all characters of the target are present in the source// If any character is not present, return -1for(char c : target.toCharArray()){if(!sourceChars[c -'a']){return-1;}}// Concatenate source until the target is a subsequence of the concatenated stringString concatenatedSource = source;int count =1;while(!isSubsequence(target, concatenatedSource)){
concatenatedSource += source;
count++;}// Number of concatenations donereturn count;}// To check if toCheck is a subsequence of the inStringpublicbooleanisSubsequence(String toCheck,String inString){int i =0, j =0;while(i < toCheck.length()&& j < inString.length()){if(toCheck.charAt(i)== inString.charAt(j)){
i++;}
j++;}return i == toCheck.length();}}
classSolution{public:intshortestWay(string source, string target){// Boolean array to mark all characters of the sourcebool sourceChars[26]={false};for(char c : source){
sourceChars[c -'a']=true;}// Check if all characters of the target are present in the source// If any character is not present, return -1for(char c : target){if(!sourceChars[c -'a']){return-1;}}// Concatenate source until the target is a subsequence of the concatenated string
string concatenatedSource = source;int count =1;while(!isSubsequence(target, concatenatedSource)){
concatenatedSource += source;
count++;}// Number of concatenations donereturn count;}// To check if toCheck is a subsequence of inStringboolisSubsequence(string toCheck, string inString){int i =0, j =0;while(i < toCheck.length()&& j < inString.length()){if(toCheck[i]== inString[j]){
i++;}
j++;}return i == toCheck.length();}};
classSolution{/**
* @param {string} source
* @param {string} target
* @return {number}
*/shortestWay(source, target){// Boolean array to mark all characters of sourcelet sourceChars =newArray(26).fill(false);for(let c of source){
sourceChars[c.charCodeAt(0)-'a'.charCodeAt(0)]=true;}// Check if all characters of target are present in source// If any character is not present, return -1for(let c of target){if(!sourceChars[c.charCodeAt(0)-'a'.charCodeAt(0)]){return-1;}}// Concatenate source until target is a subsequence of concatenated stringlet concatenatedSource = source;let count =1;while(!this.isSubsequence(target, concatenatedSource)){
concatenatedSource += source;
count++;}// Number of concatenations donereturn count;}// To check if toCheck is a subsequence of inStringisSubsequence(toCheck, inString){let i =0, j =0;while(i < toCheck.length && j < inString.length){if(toCheck[i]== inString[j]){
i++;}
j++;}return i == toCheck.length;}}
publicclassSolution{publicintShortestWay(string source,string target){// Boolean array to mark all characters of sourcebool[] sourceChars =newbool[26];foreach(char c in source){
sourceChars[c -'a']=true;}// Check if all characters of target are present in source// If any character is not present, return -1foreach(char c in target){if(!sourceChars[c -'a']){return-1;}}// Concatenate source until target is a subsequence of concatenated stringstring concatenatedSource = source;int count =1;while(!IsSubsequence(target, concatenatedSource)){
concatenatedSource += source;
count++;}// Number of concatenations donereturn count;}// To check if toCheck is a subsequence of inStringprivateboolIsSubsequence(string toCheck,string inString){int i =0, j =0;while(i < toCheck.Length && j < inString.Length){if(toCheck[i]== inString[j]){
i++;}
j++;}return i == toCheck.Length;}}
funcshortestWay(source string, target string)int{// Boolean array to mark all characters of source
sourceChars :=make([]bool,26)for_, c :=range source {
sourceChars[c-'a']=true}// Check if all characters of target are present in source// If any character is not present, return -1for_, c :=range target {if!sourceChars[c-'a']{return-1}}// To check if toCheck is a subsequence of inString
isSubsequence :=func(toCheck, inString string)bool{
i, j :=0,0for i <len(toCheck)&& j <len(inString){if toCheck[i]== inString[j]{
i++}
j++}return i ==len(toCheck)}// Concatenate source until target is a subsequence of concatenated string
concatenatedSource := source
count :=1for!isSubsequence(target, concatenatedSource){
concatenatedSource += source
count++}// Number of concatenations donereturn count
}
class Solution {funshortestWay(source: String, target: String): Int {// Boolean array to mark all characters of sourceval sourceChars =BooleanArray(26)for(c in source){
sourceChars[c -'a']=true}// Check if all characters of target are present in source// If any character is not present, return -1for(c in target){if(!sourceChars[c -'a']){return-1}}// To check if toCheck is a subsequence of inStringfunisSubsequence(toCheck: String, inString: String): Boolean {var i =0var j =0while(i < toCheck.length && j < inString.length){if(toCheck[i]== inString[j]){
i++}
j++}return i == toCheck.length
}// Concatenate source until target is a subsequence of concatenated stringvar concatenatedSource = source
var count =1while(!isSubsequence(target, concatenatedSource)){
concatenatedSource += source
count++}// Number of concatenations donereturn count
}}
classSolution{funcshortestWay(_ source:String,_ target:String)->Int{// Boolean array to mark all characters of sourcevar sourceChars =[Bool](repeating:false, count:26)for c in source {
sourceChars[Int(c.asciiValue!-Character("a").asciiValue!)]=true}// Check if all characters of target are present in source// If any character is not present, return -1for c in target {if!sourceChars[Int(c.asciiValue!-Character("a").asciiValue!)]{return-1}}// To check if toCheck is a subsequence of inStringfuncisSubsequence(_ toCheck:String,_ inString:String)->Bool{let toCheckArr =Array(toCheck)let inStringArr =Array(inString)var i =0, j =0while i < toCheckArr.count && j < inStringArr.count {if toCheckArr[i]== inStringArr[j]{
i +=1}
j +=1}return i == toCheckArr.count
}// Concatenate source until target is a subsequence of concatenated stringvar concatenatedSource = source
var count =1while!isSubsequence(target, concatenatedSource){
concatenatedSource += source
count +=1}// Number of concatenations donereturn count
}}
Time & Space Complexity
Time complexity: O(T2⋅S)
Space complexity: O(TS)
where S is the length of source and T is the length of target
2. Two Pointers
Intuition
Instead of building a huge concatenated string, we can simulate the process more efficiently. We iterate through target character by character, and for each character, we scan through source to find it. When we reach the end of source without fully matching target, we wrap around to the beginning of source and increment our count. Using modulo arithmetic, we can treat source as circular without actually concatenating it.
Algorithm
Create a set of characters in source. Return -1 if any character in target is missing.
Initialize sourceIterator = 0 and count = 0.
For each character in target:
If sourceIterator == 0, we are starting a new pass through source, so increment count.
While source[sourceIterator] does not match the current character:
Move sourceIterator forward using modulo.
If sourceIterator wraps to 0, increment count.
After finding the match, advance sourceIterator by one (with modulo).
classSolution:defshortestWay(self, source:str, target:str)->int:# Set of all characters of source. Can use a boolean array too.
source_chars =set(source)# Check if all characters of target are present in source# If any character is not present, return -1for char in target:if char notin source_chars:return-1# Length of source to loop back to start of source using mod
m =len(source)# Pointer for source
source_iterator =0# Number of times source is traversed. It will be incremented when# while finding the occurrence of a character in target, source_iterator# reaches the start of source again.
count =0# Find all characters of target in sourcefor char in target:# If while finding, iterator reaches start of source again,# increment countif source_iterator ==0:
count +=1# Find the first occurrence of char in sourcewhile source[source_iterator]!= char:# Formula for incrementing while looping back to start.
source_iterator =(source_iterator +1)% m
# If while finding, iterator reaches the start of source again,# increment countif source_iterator ==0:
count +=1# Loop will break when char is found in source. Thus, increment.# Don't increment count until it is not clear that target has# remaining characters.
source_iterator =(source_iterator +1)% m
# Return countreturn count
classSolution{publicintshortestWay(String source,String target){// Boolean array to mark all characters of sourceboolean[] sourceChars =newboolean[26];for(char c : source.toCharArray()){
sourceChars[c -'a']=true;}// Check if all characters of target are present in source// If any character is not present, return -1for(char c : target.toCharArray()){if(!sourceChars[c -'a']){return-1;}}// Length of source to loop back to start of source using modint m = source.length();// Pointer for sourceint sourceIterator =0;// Number of times source is traversed. It will be incremented when// While finding occurrence of a character in target, sourceIterator// reaches the start of source again.int count =0;// Find all characters of target in sourcefor(char c : target.toCharArray()){// If while finding, the iterator reaches the start of source again,// increment countif(sourceIterator ==0){
count++;}// Find the first occurrence of c in sourcewhile(source.charAt(sourceIterator)!= c){// Formula for incrementing while looping back to start.
sourceIterator =(sourceIterator +1)% m;// If while finding, iterator reaches start of source again,// increment countif(sourceIterator ==0){
count++;}}// Loop will break when c is found in source. Thus, increment.// Don't increment count until it is not clear that target has// remaining characters.
sourceIterator =(sourceIterator +1)% m;}// Return countreturn count;}}
classSolution{public:intshortestWay(string source, string target){// Boolean array to mark all characters of sourcebool sourceChars[26]={false};for(char c : source){
sourceChars[c -'a']=true;}// Check if all characters of target are present in source// If any character is not present, return -1for(char c : target){if(!sourceChars[c -'a']){return-1;}}// Length of source to loop back to start of source using modint m = source.length();// Pointer for sourceint sourceIterator =0;// Number of times source is traversed. It will be incremented when// while finding occurrence of a character in target, sourceIterator// reaches the start of source again.int count =0;// Find all characters of target in sourcefor(char c : target){// If while finding, iterator reaches start of source again,// increment countif(sourceIterator ==0){
count++;}// Find the first occurrence of c in sourcewhile(source[sourceIterator]!= c){// Formula for incrementing while looping back to start.
sourceIterator =(sourceIterator +1)% m;// If while finding, iterator reaches start of source again,// increment countif(sourceIterator ==0){
count++;}}// Loop will break when c is found in source. Thus, increment.// Don't increment count until it is not clear that target has// remaining characters.
sourceIterator =(sourceIterator +1)% m;}// Return countreturn count;}};
classSolution{/**
* @param {string} source
* @param {string} target
* @return {number}
*/shortestWay(source, target){// Boolean array to mark all characters of sourcelet sourceChars =newArray(26).fill(false);for(let c of source){
sourceChars[c.charCodeAt(0)-'a'.charCodeAt(0)]=true;}// Check if all characters of target are present in source// If any character is not present, return -1for(let c of target){if(!sourceChars[c.charCodeAt(0)-'a'.charCodeAt(0)]){return-1;}}// Length of source to loop back to start of source using modlet m = source.length;// Pointer for sourcelet sourceIterator =0;// Number of times source is traversed. It will be incremented when// while finding occurrence of a character in target, sourceIterator// reaches the start of source again.let count =0;// Find all characters of target in sourcefor(let c of target){// If while finding, iterator reaches start of source again,// increment countif(sourceIterator ==0){
count++;}// Find the first occurrence of c in sourcewhile(source[sourceIterator]!= c){// Formula for incrementing while looping back to start.
sourceIterator =(sourceIterator +1)% m;// If while finding, iterator reaches start of source again,// increment countif(sourceIterator ==0){
count++;}}// Loop will break when c is found in source. Thus, increment.// Don't increment count until it is not clear that target has// remaining characters.
sourceIterator =(sourceIterator +1)% m;}// Return countreturn count;}}
publicclassSolution{publicintShortestWay(string source,string target){// Boolean array to mark all characters of sourcebool[] sourceChars =newbool[26];foreach(char c in source){
sourceChars[c -'a']=true;}// Check if all characters of target are present in source// If any character is not present, return -1foreach(char c in target){if(!sourceChars[c -'a']){return-1;}}// Length of source to loop back to start of source using modint m = source.Length;// Pointer for sourceint sourceIterator =0;// Number of times source is traversedint count =0;// Find all characters of target in sourceforeach(char c in target){// If while finding, iterator reaches start of source again, increment countif(sourceIterator ==0){
count++;}// Find the first occurrence of c in sourcewhile(source[sourceIterator]!= c){
sourceIterator =(sourceIterator +1)% m;if(sourceIterator ==0){
count++;}}
sourceIterator =(sourceIterator +1)% m;}return count;}}
funcshortestWay(source string, target string)int{// Boolean array to mark all characters of source
sourceChars :=make([]bool,26)for_, c :=range source {
sourceChars[c-'a']=true}// Check if all characters of target are present in source// If any character is not present, return -1for_, c :=range target {if!sourceChars[c-'a']{return-1}}// Length of source to loop back to start of source using mod
m :=len(source)// Pointer for source
sourceIterator :=0// Number of times source is traversed
count :=0// Find all characters of target in sourcefor_, c :=range target {// If while finding, iterator reaches start of source again, increment countif sourceIterator ==0{
count++}// Find the first occurrence of c in sourcefor source[sourceIterator]!=byte(c){
sourceIterator =(sourceIterator +1)% m
if sourceIterator ==0{
count++}}
sourceIterator =(sourceIterator +1)% m
}return count
}
class Solution {funshortestWay(source: String, target: String): Int {// Boolean array to mark all characters of sourceval sourceChars =BooleanArray(26)for(c in source){
sourceChars[c -'a']=true}// Check if all characters of target are present in source// If any character is not present, return -1for(c in target){if(!sourceChars[c -'a']){return-1}}// Length of source to loop back to start of source using modval m = source.length
// Pointer for sourcevar sourceIterator =0// Number of times source is traversedvar count =0// Find all characters of target in sourcefor(c in target){if(sourceIterator ==0){
count++}while(source[sourceIterator]!= c){
sourceIterator =(sourceIterator +1)% m
if(sourceIterator ==0){
count++}}
sourceIterator =(sourceIterator +1)% m
}return count
}}
classSolution{funcshortestWay(_ source:String,_ target:String)->Int{// Boolean array to mark all characters of sourcevar sourceChars =[Bool](repeating:false, count:26)let sourceArr =Array(source)let targetArr =Array(target)for c in sourceArr {
sourceChars[Int(c.asciiValue!-Character("a").asciiValue!)]=true}// Check if all characters of target are present in source// If any character is not present, return -1for c in targetArr {if!sourceChars[Int(c.asciiValue!-Character("a").asciiValue!)]{return-1}}// Length of source to loop back to start of source using modlet m = sourceArr.count
// Pointer for sourcevar sourceIterator =0// Number of times source is traversedvar count =0// Find all characters of target in sourcefor c in targetArr {if sourceIterator ==0{
count +=1}while sourceArr[sourceIterator]!= c {
sourceIterator =(sourceIterator +1)% m
if sourceIterator ==0{
count +=1}}
sourceIterator =(sourceIterator +1)% m
}return count
}}
Time & Space Complexity
Time complexity: O(S⋅T)
Space complexity: O(1) constant space used
where S is the length of source and T is the length of target
3. Inverted Index and Binary Search
Intuition
The two-pointer approach scans through source linearly for each character in target, which can be slow. We can speed this up by precomputing the positions of each character in source. For any character we need to find, we use binary search to quickly locate the next occurrence at or after our current position. If no such occurrence exists, we wrap to the beginning of source and start a new subsequence.
Algorithm
Build an inverted index: for each character, store a sorted list of its positions in source.
Initialize sourceIterator = 0 and count = 1.
For each character in target:
If the character does not exist in source, return -1.
Binary search for the smallest index in the character's position list that is >= sourceIterator.
If no such index exists (we have passed all occurrences), increment count and use the first occurrence.
Update sourceIterator to be one past the found index.
classSolution{publicintshortestWay(String source,String target){// List of indices for all characters in sourceArrayList<Integer>[] charToIndices =newArrayList[26];for(int i =0; i < source.length(); i++){char c = source.charAt(i);if(charToIndices[c -'a']==null){
charToIndices[c -'a']=newArrayList<>();}
charToIndices[c -'a'].add(i);}// Pointer for sourceint sourceIterator =0;// Number of times we need to iterate through sourceint count =1;// Find all characters of target in sourcefor(char c : target.toCharArray()){// If the character is not in the source, return -1if(charToIndices[c -'a']==null){return-1;}// Binary search to find the index of the character in source// next to the source iteratorArrayList<Integer> indices = charToIndices[c -'a'];int index =Collections.binarySearch(indices, sourceIterator);// If the index is negative, we need to find the next index// that is greater than the source iteratorif(index <0){
index =-index -1;}// If we have reached the end of the list, we need to iterate// through source again, hence first index of character in source.if(index == indices.size()){
count++;
sourceIterator = indices.get(0)+1;}else{
sourceIterator = indices.get(index)+1;}}// Return the number of times we need to iterate through sourcereturn count;}}
classSolution{public:intshortestWay(string source, string target){// Array to store the vector of charToIndices of each character in source
vector <int> charToIndices[26];for(int i =0; i < source.size(); i++){
charToIndices[source[i]-'a'].push_back(i);}// The current index in sourceint sourceIterator =0;// Number of times we have to iterate through source to get targetint count =1;// Find all characters of target in sourcefor(int i =0; i < target.size(); i++){// If the character is not present in source, return -1if(charToIndices[target[i]-'a'].size()==0){return-1;}// Binary search to find the index of the character in source next to the source iterator
vector <int> indices = charToIndices[target[i]-'a'];int index =lower_bound(indices.begin(), indices.end(), sourceIterator)- indices.begin();// If we have reached the end of the list, we need to iterate// through source again, hence first index of character in source.if(index == indices.size()){
count++;
sourceIterator = indices[0]+1;}else{
sourceIterator = indices[index]+1;}}return count;}};
classSolution{/**
* @param {string} source
* @param {string} target
* @return {number}
*/shortestWay(source, target){// List of indices for all characters in sourcelet charToIndices =newArray(26);for(let i =0; i < source.length; i++){let c = source[i];if(charToIndices[c.charCodeAt(0)-'a'.charCodeAt(0)]==null){
charToIndices[c.charCodeAt(0)-'a'.charCodeAt(0)]=[];}
charToIndices[c.charCodeAt(0)-'a'.charCodeAt(0)].push(i);}// Pointer for sourcelet sourceIterator =0;// Number of times we need to iterate through sourcelet count =1;// Find all characters of target in sourcefor(let i =0; i < target.length; i++){let c = target[i];// If the character is not in the source, return -1if(charToIndices[c.charCodeAt(0)-'a'.charCodeAt(0)]==null){return-1;}// Binary search to find the index of the character in source// next to the source iteratorlet indices = charToIndices[c.charCodeAt(0)-'a'.charCodeAt(0)];let index =this.binarySearch(indices, sourceIterator);// If we have reached the end of the list, we need to iterate// through source again, hence first index of character in source.if(index == indices.length){
count++;
sourceIterator = indices[0]+1;}else{
sourceIterator = indices[index]+1;}}// Return the number of times we need to iterate through sourcereturn count;}/**
* @param {number[]} arr
* @param {number} target
* @return {number}
*/binarySearch(arr, target){let left =0;let right = arr.length -1;while(left <= right){let mid = Math.floor((left + right)/2);if(arr[mid]== target){return mid;}elseif(arr[mid]< target){
left = mid +1;}else{
right = mid -1;}}return left;}}
publicclassSolution{publicintShortestWay(string source,string target){// List of indices for all characters in sourceList<int>[] charToIndices =newList<int>[26];for(int i =0; i < source.Length; i++){char c = source[i];if(charToIndices[c -'a']==null){
charToIndices[c -'a']=newList<int>();}
charToIndices[c -'a'].Add(i);}// Pointer for sourceint sourceIterator =0;// Number of times we need to iterate through sourceint count =1;// Find all characters of target in sourceforeach(char c in target){// If the character is not in the source, return -1if(charToIndices[c -'a']==null){return-1;}// Binary search to find the index of the character in sourceList<int> indices = charToIndices[c -'a'];int index =BinarySearch(indices, sourceIterator);// If we have reached the end of the list, iterate through source againif(index == indices.Count){
count++;
sourceIterator = indices[0]+1;}else{
sourceIterator = indices[index]+1;}}return count;}privateintBinarySearch(List<int> arr,int target){int left =0, right = arr.Count -1;while(left <= right){int mid =(left + right)/2;if(arr[mid]== target){return mid;}elseif(arr[mid]< target){
left = mid +1;}else{
right = mid -1;}}return left;}}
funcshortestWay(source string, target string)int{// List of indices for all characters in source
charToIndices :=make([][]int,26)for i :=0; i <len(source); i++{
c := source[i]-'a'
charToIndices[c]=append(charToIndices[c], i)}// Binary search helper
binarySearch :=func(arr []int, target int)int{
left, right :=0,len(arr)-1for left <= right {
mid :=(left + right)/2if arr[mid]== target {return mid
}elseif arr[mid]< target {
left = mid +1}else{
right = mid -1}}return left
}// Pointer for source
sourceIterator :=0// Number of times we need to iterate through source
count :=1// Find all characters of target in sourcefor i :=0; i <len(target); i++{
c := target[i]-'a'// If the character is not in the source, return -1iflen(charToIndices[c])==0{return-1}// Binary search to find the index of the character in source
indices := charToIndices[c]
index :=binarySearch(indices, sourceIterator)// If we have reached the end of the list, iterate through source againif index ==len(indices){
count++
sourceIterator = indices[0]+1}else{
sourceIterator = indices[index]+1}}return count
}
class Solution {funshortestWay(source: String, target: String): Int {// List of indices for all characters in sourceval charToIndices = Array<MutableList<Int>?>(26){null}for(i in source.indices){val c = source[i]-'a'if(charToIndices[c]==null){
charToIndices[c]=mutableListOf()}
charToIndices[c]!!.add(i)}// Pointer for sourcevar sourceIterator =0// Number of times we need to iterate through sourcevar count =1// Find all characters of target in sourcefor(c in target){// If the character is not in the source, return -1if(charToIndices[c -'a']==null){return-1}// Binary search to find the index of the character in sourceval indices = charToIndices[c -'a']!!val index =binarySearch(indices, sourceIterator)// If we have reached the end of the list, iterate through source againif(index == indices.size){
count++
sourceIterator = indices[0]+1}else{
sourceIterator = indices[index]+1}}return count
}privatefunbinarySearch(arr: List<Int>, target: Int): Int {var left =0var right = arr.size -1while(left <= right){val mid =(left + right)/2if(arr[mid]== target){return mid
}elseif(arr[mid]< target){
left = mid +1}else{
right = mid -1}}return left
}}
classSolution{funcshortestWay(_ source:String,_ target:String)->Int{// List of indices for all characters in sourcevar charToIndices =[[Int]?](repeating:nil, count:26)let sourceArr =Array(source)let targetArr =Array(target)for i in0..<sourceArr.count {let c =Int(sourceArr[i].asciiValue!-Character("a").asciiValue!)if charToIndices[c]==nil{
charToIndices[c]=[]}
charToIndices[c]!.append(i)}// Pointer for sourcevar sourceIterator =0// Number of times we need to iterate through sourcevar count =1// Find all characters of target in sourcefor c in targetArr {let idx =Int(c.asciiValue!-Character("a").asciiValue!)// If the character is not in the source, return -1guardlet indices = charToIndices[idx]else{return-1}// Binary search to find the index of the character in sourcelet index =binarySearch(indices, sourceIterator)// If we have reached the end of the list, iterate through source againif index == indices.count {
count +=1
sourceIterator = indices[0]+1}else{
sourceIterator = indices[index]+1}}return count
}privatefuncbinarySearch(_ arr:[Int],_ target:Int)->Int{varleft=0,right= arr.count -1whileleft<=right{let mid =(left+right)/2if arr[mid]== target {return mid
}elseif arr[mid]< target {left= mid +1}else{right= mid -1}}returnleft}}
Time & Space Complexity
Time complexity: O(S+Tlog(S))
Space complexity: O(S)
where S is the length of source and T is the length of target
4. 2D Array
Intuition
We can achieve constant-time lookups by precomputing a 2D array where nextOccurrence[i][c] gives the index of the next occurrence of character c at or after position i in source. We build this array from right to left using dynamic programming: at each position, we copy the values from the next position and then update the current character's entry. This allows O(1) character lookups during the matching phase.
Algorithm
Create a 2D array nextOccurrence of size source.length x 26.
Initialize the last row: set all entries to -1, then set the entry for the last character of source to source.length - 1.
Fill the array from right to left:
Copy values from nextOccurrence[i+1] to nextOccurrence[i].
Update nextOccurrence[i][source[i]] to i.
Initialize sourceIterator = 0 and count = 1.
For each character in target:
If nextOccurrence[0][c] == -1, the character does not exist in source; return -1.
If sourceIterator == source.length or nextOccurrence[sourceIterator][c] == -1, increment count and reset sourceIterator = 0.
Set sourceIterator = nextOccurrence[sourceIterator][c] + 1.
classSolution:defshortestWay(self, source:str, target:str)->int:# Length of source
source_length =len(source)# Next Occurrence of Character after Index
next_occurrence =[defaultdict(int)for idx inrange(source_length)]# Base Case
next_occurrence[source_length -1][source[source_length -1]]= source_length -1# Using Recurrence Relation to fill next_occurrencefor idx inrange(source_length -2,-1,-1):
next_occurrence[idx]= next_occurrence[idx +1].copy()
next_occurrence[idx][source[idx]]= idx
# Pointer for source
source_iterator =0# Number of times we need to iterate through source
count =1# Find all characters of target in sourcefor char in target:# If character is not in source, return -1if char notin next_occurrence[0]:return-1# If we have reached the end of source, or the character is not in# source after source_iterator, loop back to beginningif(source_iterator == source_length or
char notin next_occurrence[source_iterator]):
count +=1
source_iterator =0# Next occurrence of character in source after source_iterator
source_iterator = next_occurrence[source_iterator][char]+1# Return the number of times we need to iterate through sourcereturn count
classSolution{publicintshortestWay(String source,String target){// Next occurrence of a character after a given indexint[][] nextOccurrence =newint[source.length()][26];// Base Casefor(int c =0; c <26; c++){
nextOccurrence[source.length()-1][c]=-1;}
nextOccurrence[source.length()-1][source.charAt(source.length()-1)-'a']= source.length()-1;// Fill using recurrence relationfor(int idx = source.length()-2; idx >=0; idx--){for(int c =0; c <26; c++){
nextOccurrence[idx][c]= nextOccurrence[idx +1][c];}
nextOccurrence[idx][source.charAt(idx)-'a']= idx;}// Pointer to the current index in sourceint sourceIterator =0;// Number of times we need to iterate through sourceint count =1;// Find all characters of target in sourcefor(char c : target.toCharArray()){// If the character is not present in sourceif(nextOccurrence[0][c -'a']==-1){return-1;}// If we have reached the end of source, or the character is not in// source after source_iterator, loop back to beginningif(sourceIterator == source.length()|| nextOccurrence[sourceIterator][c -'a']==-1){
count++;
sourceIterator =0;}// Next occurrence of character in source after source_iterator
sourceIterator = nextOccurrence[sourceIterator][c -'a']+1;}// Return the number of times we need to iterate through sourcereturn count;}}
classSolution{public:intshortestWay(string source, string target){// Next Occurrence of Character after Indexint nextOccurrence[source.length()][26];// Base Casefor(int c =0; c <26; c++){
nextOccurrence[source.length()-1][c]=-1;}
nextOccurrence[source.length()-1][source[source.length()-1]-'a']= source.length()-1;// Fill using recurrence relationfor(int idx = source.length()-2; idx >=0; idx--){for(int c =0; c <26; c++){
nextOccurrence[idx][c]= nextOccurrence[idx +1][c];}
nextOccurrence[idx][source[idx]-'a']= idx;}// Pointer to the current index in sourceint sourceIterator =0;// Number of times we need to iterate through sourceint count =1;// Find all characters of target in sourcefor(char c : target){// If the character is not present in sourceif(nextOccurrence[0][c -'a']==-1){return-1;}// If we have reached the end of source, or the character is not in// source after source_iterator, loop back to beginningif(sourceIterator == source.length()|| nextOccurrence[sourceIterator][c -'a']==-1){
count++;
sourceIterator =0;}// Next occurrence of character in source after source_iterator
sourceIterator = nextOccurrence[sourceIterator][c -'a']+1;}// Return the number of times we need to iterate through sourcereturn count;}};
classSolution{/**
* @param {string} source
* @param {string} target
* @return {number}
*/shortestWay(source, target){// Length of sourceconst sourceLength = source.length
// Next Occurrence of Character after Indexconst nextOccurrence = Array.from({length: sourceLength},()=>Array(26).fill(-1))// Base Casefor(let c =0; c <26; c++){
nextOccurrence[sourceLength -1][c]=-1}
nextOccurrence[sourceLength -1][source[sourceLength -1].charCodeAt(0)-'a'.charCodeAt(0)]= sourceLength -1// Fill using the recurrence relationfor(let idx = sourceLength -2; idx >=0; idx--){for(let c =0; c <26; c++){
nextOccurrence[idx][c]= nextOccurrence[idx +1][c]}
nextOccurrence[idx][source[idx].charCodeAt(0)-'a'.charCodeAt(0)]= idx
}// Pointer to the current index in sourcelet sourceIterator =0// Number of times we need to iterate through sourcelet count =1// Find all characters of target in sourcefor(let idx =0; idx < target.length; idx++){// If the character is not present in sourceif(nextOccurrence[0][target[idx].charCodeAt(0)-'a'.charCodeAt(0)]==-1){return-1}// If we have reached the end of source, or the character is not in// source after source_iterator, loop back to beginningif(sourceIterator == sourceLength || nextOccurrence[sourceIterator][target[idx].charCodeAt(0)-'a'.charCodeAt(0)]==-1){
count++
sourceIterator =0}// Next occurrence of the character in source after source_iterator
sourceIterator = nextOccurrence[sourceIterator][target[idx].charCodeAt(0)-'a'.charCodeAt(0)]+1}// Return the number of times we need to iterate through sourcereturn count
}}
publicclassSolution{publicintShortestWay(string source,string target){// Next occurrence of a character after a given indexint[][] nextOccurrence =newint[source.Length][];for(int i =0; i < source.Length; i++){
nextOccurrence[i]=newint[26];}// Base Casefor(int c =0; c <26; c++){
nextOccurrence[source.Length -1][c]=-1;}
nextOccurrence[source.Length -1][source[source.Length -1]-'a']= source.Length -1;// Fill using recurrence relationfor(int idx = source.Length -2; idx >=0; idx--){for(int c =0; c <26; c++){
nextOccurrence[idx][c]= nextOccurrence[idx +1][c];}
nextOccurrence[idx][source[idx]-'a']= idx;}// Pointer to the current index in sourceint sourceIterator =0;// Number of times we need to iterate through sourceint count =1;// Find all characters of target in sourceforeach(char c in target){// If the character is not present in sourceif(nextOccurrence[0][c -'a']==-1){return-1;}// If we have reached the end of source, or the character is not in// source after source_iterator, loop back to beginningif(sourceIterator == source.Length || nextOccurrence[sourceIterator][c -'a']==-1){
count++;
sourceIterator =0;}// Next occurrence of character in source after source_iterator
sourceIterator = nextOccurrence[sourceIterator][c -'a']+1;}return count;}}
funcshortestWay(source string, target string)int{
sourceLength :=len(source)// Next Occurrence of Character after Index
nextOccurrence :=make([][]int, sourceLength)for i :=range nextOccurrence {
nextOccurrence[i]=make([]int,26)for j :=range nextOccurrence[i]{
nextOccurrence[i][j]=-1}}// Base Case
nextOccurrence[sourceLength-1][source[sourceLength-1]-'a']= sourceLength -1// Fill using recurrence relationfor idx := sourceLength -2; idx >=0; idx--{for c :=0; c <26; c++{
nextOccurrence[idx][c]= nextOccurrence[idx+1][c]}
nextOccurrence[idx][source[idx]-'a']= idx
}// Pointer to the current index in source
sourceIterator :=0// Number of times we need to iterate through source
count :=1// Find all characters of target in sourcefor i :=0; i <len(target); i++{
c := target[i]-'a'// If the character is not present in sourceif nextOccurrence[0][c]==-1{return-1}// If we have reached the end of source, or the character is not in// source after source_iterator, loop back to beginningif sourceIterator == sourceLength || nextOccurrence[sourceIterator][c]==-1{
count++
sourceIterator =0}// Next occurrence of character in source after source_iterator
sourceIterator = nextOccurrence[sourceIterator][c]+1}return count
}
class Solution {funshortestWay(source: String, target: String): Int {val sourceLength = source.length
// Next occurrence of a character after a given indexval nextOccurrence =Array(sourceLength){IntArray(26){-1}}// Base Case
nextOccurrence[sourceLength -1][source[sourceLength -1]-'a']= sourceLength -1// Fill using recurrence relationfor(idx in sourceLength -2 downTo 0){for(c in0 until 26){
nextOccurrence[idx][c]= nextOccurrence[idx +1][c]}
nextOccurrence[idx][source[idx]-'a']= idx
}// Pointer to the current index in sourcevar sourceIterator =0// Number of times we need to iterate through sourcevar count =1// Find all characters of target in sourcefor(c in target){// If the character is not present in sourceif(nextOccurrence[0][c -'a']==-1){return-1}// If we have reached the end of source, or the character is not in// source after source_iterator, loop back to beginningif(sourceIterator == sourceLength || nextOccurrence[sourceIterator][c -'a']==-1){
count++
sourceIterator =0}// Next occurrence of character in source after source_iterator
sourceIterator = nextOccurrence[sourceIterator][c -'a']+1}return count
}}
classSolution{funcshortestWay(_ source:String,_ target:String)->Int{let sourceArr =Array(source)let targetArr =Array(target)let sourceLength = sourceArr.count
// Next occurrence of a character after a given indexvar nextOccurrence =[[Int]](repeating:[Int](repeating:-1, count:26), count: sourceLength)// Base Case
nextOccurrence[sourceLength -1][Int(sourceArr[sourceLength -1].asciiValue!-Character("a").asciiValue!)]= sourceLength -1// Fill using recurrence relationfor idx instride(from: sourceLength -2, through:0, by:-1){for c in0..<26{
nextOccurrence[idx][c]= nextOccurrence[idx +1][c]}
nextOccurrence[idx][Int(sourceArr[idx].asciiValue!-Character("a").asciiValue!)]= idx
}// Pointer to the current index in sourcevar sourceIterator =0// Number of times we need to iterate through sourcevar count =1// Find all characters of target in sourcefor c in targetArr {let cIdx =Int(c.asciiValue!-Character("a").asciiValue!)// If the character is not present in sourceif nextOccurrence[0][cIdx]==-1{return-1}// If we have reached the end of source, or the character is not in// source after source_iterator, loop back to beginningif sourceIterator == sourceLength || nextOccurrence[sourceIterator][cIdx]==-1{
count +=1
sourceIterator =0}// Next occurrence of character in source after source_iterator
sourceIterator = nextOccurrence[sourceIterator][cIdx]+1}return count
}}
Time & Space Complexity
Time complexity: O(S+T)
Space complexity: O(S)
where S is the length of source and T is the length of target
Common Pitfalls
Forgetting to Check for Impossible Characters
Before attempting to form the target, you must verify that every character in target exists in source. If any character is missing, forming the target is impossible regardless of how many times you concatenate source. Skipping this check leads to infinite loops or incorrect results.
Off-by-One Errors When Wrapping Around Source
When using a pointer to track your position in source, a common mistake is mishandling the wrap-around logic. If you reach the end of source without finding the needed character, you must reset to the beginning and increment the count. Errors often occur when forgetting to increment the count upon wrap-around or when incorrectly resetting the pointer position.
Counting Subsequences Incorrectly
Another pitfall is miscounting the number of source copies needed. The count should increment each time you start a new pass through source, not each time you find a character. Starting with count = 0 instead of count = 1 or incrementing at the wrong point in the loop results in an off-by-one error in the final answer.