Given a string S of length N and an array Q[][] of dimension M × 3 consisting of queries of type {L, R, C}, the task is to print the first index of the character C in the range [L, R], if found. Otherwise, print -1.
Examples:
Input: S= “abcabcabc”, Q[][] = { { 0, 3, ‘a’ }, { 0, 2, ‘b’ }, { 2, 4, ‘z’ } }
Output: 0 1 -1
Explanation:
- First query: Print 0 which is the first index of character ‘a’ in the range [0, 3].
- Second query: Print 1, which is the first index of character ‘b’ in the range [0, 2].
- Third query: Print -1 as the character ‘z’ does not occur in the range [2, 4].
Input: S= “neveropen”, Q[][] = { { 0, 12, ‘f’ }, { 0, 2, ‘g’ }, { 6, 12, ‘f’ } }
Output: 5 0 -1
Explanation:
- First query: Print 5, which is the first index of character ‘f’ in the range [0, 12].
- Second query: Print 0 which is
the first index of character ‘g’ in the range [0, 2].- Third query: Print -1 as the character ‘f’ does not occur in the range [6, 12].
Naive Approach: The simplest approach is to traverse the string over the range of indices [L, R] for each query and print the first occurrence of character C if found. Otherwise, print -1.
Time Complexity: O(M * Q)
Auxiliary Space: O(1)
Efficient Approach: The above approach can be optimized by pre-storing the indices of characters in the map of vectors and using binary search to find the index in the range [L, R] in the vector of characters C. Follow the steps below to solve the problem:
- Initialize a Map < char, vector < int > >, say V, to store indices of all occurrences of a character.
- Traverse the string and update V.
- Traverse the array Q:
- If the size of V[C] is zero then print -1.
- Otherwise, find the index by using binary search in vector V[C] i.e lower_bound(V[C].begin(), V[C].end(), L) – V[C].begin() and store it in a variable, say idx.
- If idx = N or idx > R, then print -1.
- Otherwise, print the index idx.
Below is the implementation of the above approach:
C++
// C++ implementation // for the above approach #include <bits/stdc++.h> using namespace std; // Function to find the first occurrence // for a given range int firstOccurrence(string s, vector<pair<pair< int , int >, char > > Q) { // N = length of string int N = s.size(); // M = length of queries int M = Q.size(); // Stores the indices of a character map< char , vector< int > > v; // Traverse the string for ( int i = 0; i < N; i++) { // Push the index i // into the vector[s[i]] v[s[i]].push_back(i); } // Traverse the query for ( int i = 0; i < M; i++) { // Stores the value L int left = Q[i].first.first; // Stores the value R int right = Q[i].first.second; // Stores the character C char c = Q[i].second; if (v.size() == 0) { cout << "-1 " ; continue ; } // Find index >= L in // the vector v int idx = lower_bound(v.begin(), v.end(), left) - v.begin(); // If there is no index of C >= L if (idx == ( int )v.size()) { cout << "-1 " ; continue ; } // Stores the value at idx idx = v[idx]; // If idx > R if (idx > right) { cout << "-1 " ; } // Otherwise else { cout << idx << " " ; } } } // Driver Code int main() { string S = "abcabcabc" ; vector<pair<pair< int , int >, char > > Q = { { { 0, 3 }, 'a' }, { { 0, 2 }, 'b' }, { { 2, 4 }, 'z' } }; firstOccurrence(S, Q); return 0; } |
Java
// Java program to implement the above approach import java.util.*; public class Main { // Function to find the first occurrence // for a given range public static void firstOccurrence(String s, ArrayList<Pair<Pair<Integer, Integer>, Character>> Q) { // N = length of string int N = s.length(); // M = length of queries int M = Q.size(); // Stores the indices of a character HashMap<Character, ArrayList<Integer>> v = new HashMap<Character, ArrayList<Integer>>(); // Traverse the string for ( int i = 0 ; i < N; i++) { // Push the index i // into the vector[s.charAt(i)] char c = s.charAt(i); if (!v.containsKey(c)) { v.put(c, new ArrayList<Integer>()); } v.get(c).add(i); } // Traverse the query for ( int i = 0 ; i < M; i++) { // Stores the value L int left = Q.get(i).first.first; // Stores the value R int right = Q.get(i).first.second; // Stores the character C char c = Q.get(i).second; if (!v.containsKey(c) || v.get(c).size() == 0 ) { System.out.print( "-1 " ); continue ; } // Find index >= L in // the vector v.get(c) ArrayList<Integer> charIndices = v.get(c); int idx = Collections.binarySearch(charIndices, left); // If there is no index of C >= L if (idx < 0 ) { idx = -(idx + 1 ); if (idx == charIndices.size() || charIndices.get(idx) > right) { System.out.print( "-1 " ); continue ; } } else if (charIndices.get(idx) > right) { System.out.print( "-1 " ); continue ; } // Stores the value at idx idx = charIndices.get(idx); // If idx > R if (idx > right) { System.out.print( "-1 " ); } // Otherwise else { System.out.print(idx + " " ); } } } // Driver Code public static void main(String[] args) { String S = "abcabcabc" ; ArrayList<Pair<Pair<Integer, Integer>, Character>> Q = new ArrayList<Pair<Pair<Integer, Integer>, Character>>(); Q.add( new Pair<Pair<Integer, Integer>, Character>( new Pair<Integer, Integer>( 0 , 3 ), 'a' )); Q.add( new Pair<Pair<Integer, Integer>, Character>( new Pair<Integer, Integer>( 0 , 2 ), 'b' )); Q.add( new Pair<Pair<Integer, Integer>, Character>( new Pair<Integer, Integer>( 2 , 4 ), 'z' )); firstOccurrence(S, Q); } } class Pair<U, V> { public U first; public V second; public Pair(U first, V second) { this .first = first; this .second = second; } } // Contributed by adityashae15 |
Python3
# Python3 implementation # for the above approach from bisect import bisect_left # Function to find the first occurrence # for a given range def firstOccurrence(s, Q): # N = length of string N = len (s) # M = length of queries M = len (Q) # Stores the indices of a character v = [[] for i in range ( 26 )] # Traverse the string for i in range (N): # Push the index i # into the vector[s[i]] v[ ord (s[i]) - ord ( 'a' )].append(i) # Traverse the query for i in range (M): # Stores the value L left = Q[i][ 0 ] # Stores the value R right = Q[i][ 1 ] # Stores the character C c = Q[i][ 2 ] if ( len (v[ ord (c) - ord ( 'a' )]) = = 0 ): print ( "-1" ) continue # Find index >= L in # the vector v idx = bisect_left(v[ ord (c) - ord ( 'a' )], left) # If there is no index of C >= L if (idx = = len (v[ ord (c) - ord ( 'a' )])): print ( "-1 " ) continue # Stores the value at idx idx = v[ ord (c) - ord ( 'a' )][idx] # If idx > R if (idx > right): print ( "-1 " ) # Otherwise else : print (idx, end = " " ) # Driver Code if __name__ = = '__main__' : S = "abcabcabc" ; Q = [ [ 0 , 3 , 'a' ],[ 0 , 2 , 'b' ],[ 2 , 4 , 'z' ]] firstOccurrence(S, Q) # This code is contributed by mohit kumar 29. |
C#
// C# program to implement the above approach using System; using System.Collections.Generic; public class Program { // Function to find the first occurrence // for a given range public static void FirstOccurrence( string s, List<Tuple<Tuple< int , int >, char >> Q) { // N = length of string int N = s.Length; // M = length of queries int M = Q.Count; // Stores the indices of a character Dictionary< char , List< int >> v = new Dictionary< char , List< int >>(); // Traverse the string for ( int i = 0; i < N; i++) { // Push the index i // into the vector[s.charAt(i)] char c = s[i]; if (!v.ContainsKey(c)) { v = new List< int >(); } v.Add(i); } // Traverse the query for ( int i = 0; i < M; i++) { // Stores the value L int left = Q[i].Item1.Item1; // Stores the value R int right = Q[i].Item1.Item2; // Stores the character C char c = Q[i].Item2; if (!v.ContainsKey(c) || v.Count == 0) { Console.Write( "-1 " ); continue ; } // Find index >= L in // the vector v.get(c) List< int > charIndices = v; int idx = charIndices.BinarySearch(left); // If there is no index of C >= L if (idx < 0) { idx = -(idx + 1); if (idx == charIndices.Count || charIndices[idx] > right) { Console.Write( "-1 " ); continue ; } } else if (charIndices[idx] > right) { Console.Write( "-1 " ); continue ; } // Stores the value at idx idx = charIndices[idx]; // If idx > R if (idx > right) { Console.Write( "-1 " ); } // Otherwise else { Console.Write(idx + " " ); } } } // Driver Code public static void Main() { string S = "abcabcabc" ; List<Tuple<Tuple< int , int >, char >> Q = new List<Tuple<Tuple< int , int >, char >>(); Q.Add(Tuple.Create(Tuple.Create(0, 3), 'a' )); Q.Add(Tuple.Create(Tuple.Create(0, 2), 'b' )); Q.Add(Tuple.Create(Tuple.Create(2, 4), 'z' )); FirstOccurrence(S, Q); } } // Contributed by adityasharmadev01 |
Javascript
// javascript implementation // for the above approach // lower_bound function function lower_bound(a, x){ let l = 0; let h = a.length - 1; while (l <= h){ let m = Math.floor((l + h)/2); if (a[m] < x){ l = m + 1; } else if (a[m] > x){ h = m - 1; } else return l; } return l; } // Function to find the first occurrence // for a given range function firstOccurrence(s, Q) { // N = length of string let N = s.length; // M = length of queries let M = Q.length; // Stores the indices of a character let v = {}; // map<char, vector<int> > v; // Traverse the string for (let i = 0; i < N; i++) { // Push the index i // into the vector[s[i]] if (s[i] in v){ v[s[i]].push(i); } else { v[s[i]] = []; v[s[i]].push(i); } } // Traverse the query for (let i = 0; i < M; i++) { // Stores the value L let left = Q[i][0][0]; // Stores the value R let right = Q[i][0][1]; // Stores the character C let c = Q[i][1]; if (c in v){ } else { console.log( "-1 " ); continue ; } // Find index >= L in // the vector v let idx = lower_bound(v, left); // If there is no index of C >= L if (idx == v.length) { process.stdout.write( "-1 " ); continue ; } // Stores the value at idx idx = v[idx]; // If idx > R if (idx > right) { process.stdout.write( "-1 " ); } // Otherwise else { process.stdout.write(idx + " " ); } } } // Driver Code let S = "abcabcabc" ; let Q = [ [ [ 0, 3 ], 'a' ], [ [ 0, 2 ], 'b' ], [ [ 2, 4 ], 'z' ] ]; firstOccurrence(S, Q); // The code is contributed by Arushi Jindal. |
0 1 -1
Time Complexity: O(M * log(N))
Auxiliary Space: O(N)
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 neveropen!