Precomputation in strings refers to the process of performing calculations on a given string before actual operations or queries are performed.
The goal of precomputation is to process and store certain information about the string that can be used to optimize and speed up various string-related operations or queries that might be performed later. By pre-computing certain information of the string, redundant calculations can be avoided, and faster runtime for specific tasks can be achieved. Now let’s consider the following problem for better understanding.
Problem Statement:
Given a string s of length n and a set of q queries, each query defined by a triplet (l, r, c) where: For each query, you need to calculate and output the number of occurrences of the character c within the substring s[l...r] .
Naïve Approach:
The idea is to iterate from index l to r and count the frequency of character c for each query separately.
For each query, this approach requires iterating through the substring characters and checking if each character is equal to the given character c . The time complexity of this approach for a single query is O(n). Since there are q queries, the overall time complexity is O(q * n).
The naïve approach is simple to implement, but it might be inefficient for large values of q and m since it involves repetitive calculations for each query.
Approach (Using PreComputation):
To efficiently solve this problem, precomputation technique can be used involving the use of a 2D array that stores prefix sums of each character occurrences. Then, for each query, count of the desired character within the specified substring can be computed in O(1) time using the prefix sum.
Steps to implement the above idea:
- Initialize a 2D array
prefixSum of size n x 26 , where n is the length of the input string s , and 26 represents the number of English alphabet characters (‘a’ to ‘z’).
- prefixSum[i][ch] denotes the number of times character ch occurs till i.
- For each index
i from 0 to n - 1 :
- For each character
ch from ‘a‘ to ‘z‘, calculate prefixSum[i][ch - 'a'] by adding prefixSum[i - 1][ch - 'a'] to the count of character ch at index i in string s .
- For each query
(l, r, c) :
- The count of character
c is prefixSum[r] - prefixSum[l - 1] .
Below is the implementation of above approach:
C++
#include <bits/stdc++.h>
using namespace std;
vector< int > preComputation(string s,
vector<vector< int > > queries,
vector< char > character)
{
int n = s.size();
vector<vector< int > > psum(n + 1, vector< int >(26, 0));
for ( int i = 1; i <= n; i++) {
for ( int j = 0; j < 26; j++) {
psum[i][j] += psum[i - 1][j];
}
psum[i][s[i - 1] - 97]++;
}
int m = queries.size();
vector< int > ans(m);
for ( int i = 0; i < m; i++) {
ans[i]
= psum[queries[i][1]][character[i] - 97]
- psum[queries[i][0] - 1][character[i] - 97];
}
return ans;
}
int main()
{
string s = "baabcaba";
vector<vector< int > > queries{
{ 2, 6 }, { 4, 5 }, { 1, 6 }, { 3, 6 }
};
vector< char > character{ 'a' , 'a' , 'b' , 'c' };
vector< int > ans = preComputation(s, queries, character);
for ( int i = 0; i < ans.size(); i++) {
cout << "Frequency of character " << character[i]
<< " in the range " << queries[i][0] << " to "
<< queries[i][1] << " :" << ans[i] << "\n";
}
}
|
Java
public class PrefixSum {
static int [] preComputation(String s, int [][] queries, char [] characters) {
int n = s.length();
int [][] psum = new int [n + 1 ][ 26 ];
for ( int i = 1 ; i <= n; i++) {
for ( int j = 0 ; j < 26 ; j++) {
psum[i][j] = psum[i - 1 ][j];
}
psum[i][s.charAt(i - 1 ) - 'a' ]++;
}
int m = queries.length;
int [] ans = new int [m];
for ( int i = 0 ; i < m; i++) {
ans[i] = psum[queries[i][ 1 ]][characters[i] - 'a' ] - psum[queries[i][ 0 ] - 1 ][characters[i] - 'a' ];
}
return ans;
}
public static void main(String[] args) {
String s = "baabcaba" ;
int [][] queries = {{ 2 , 6 }, { 4 , 5 }, { 1 , 6 }, { 3 , 6 }};
char [] characters = { 'a' , 'a' , 'b' , 'c' };
int [] ans = preComputation(s, queries, characters);
for ( int i = 0 ; i < ans.length; i++) {
System.out.println( "Frequency of character " + characters[i] + " in the range "
+ queries[i][ 0 ] + " to " + queries[i][ 1 ] + ": " + ans[i]);
}
}
}
|
Python3
def pre_computation(s, queries, characters):
n = len (s)
psum = [[ 0 ] * 26 for _ in range (n + 1 )]
for i in range ( 1 , n + 1 ):
for j in range ( 26 ):
psum[i][j] = psum[i - 1 ][j]
psum[i][ ord (s[i - 1 ]) - ord ( 'a' )] + = 1
m = len (queries)
ans = [ 0 ] * m
for i in range (m):
ans[i] = psum[queries[i][ 1 ]][ ord (characters[i]) - ord ( 'a' )] - psum[queries[i][ 0 ] - 1 ][ ord (characters[i]) - ord ( 'a' )]
return ans
if __name__ = = "__main__" :
s = "baabcaba"
queries = [[ 2 , 6 ], [ 4 , 5 ], [ 1 , 6 ], [ 3 , 6 ]]
characters = [ 'a' , 'a' , 'b' , 'c' ]
ans = pre_computation(s, queries, characters)
for i in range ( len (ans)):
print (f "Frequency of character {characters[i]} in the range {queries[i][0]} to {queries[i][1]}: {ans[i]}" )
|
C#
using System;
using System.Collections.Generic;
class GFG
{
static List< int > PreComputation( string s, List<List< int >> queries, List< char > character)
{
int n = s.Length;
List<List< int >> psum = new List<List< int >>(n + 1);
for ( int i = 0; i <= n; i++)
{
psum.Add( new List< int >(26));
for ( int j = 0; j < 26; j++)
{
psum[i].Add(0);
}
}
for ( int i = 1; i <= n; i++)
{
for ( int j = 0; j < 26; j++)
{
psum[i][j] += psum[i - 1][j];
}
psum[i][s[i - 1] - 'a' ]++;
}
int m = queries.Count;
List< int > ans = new List< int >(m);
for ( int i = 0; i < m; i++)
{
ans.Add(psum[queries[i][1]][character[i] - 'a' ] - psum[queries[i][0] - 1][character[i] - 'a' ]);
}
return ans;
}
static void Main()
{
string s = "baabcaba" ;
List<List< int >> queries = new List<List< int >>
{
new List< int > {2, 6},
new List< int > {4, 5},
new List< int > {1, 6},
new List< int > {3, 6}
};
List< char > character = new List< char > { 'a' , 'a' , 'b' , 'c' };
List< int > ans = PreComputation(s, queries, character);
for ( int i = 0; i < ans.Count; i++)
{
Console.WriteLine($ "Frequency of character {character[i]} in the range {queries[i][0]} to {queries[i][1]}: {ans[i]}" );
}
}
}
|
Javascript
function preComputation(s, queries, character) {
const n = s.length;
const psum = new Array(n + 1).fill(0).map(() => new Array(26).fill(0));
for (let i = 1; i <= n; i++) {
for (let j = 0; j < 26; j++) {
psum[i][j] += psum[i - 1][j];
}
const charIndex = s.charCodeAt(i - 1) - 97;
psum[i][charIndex]++;
}
const m = queries.length;
const ans = new Array(m);
for (let i = 0; i < m; i++) {
const charIndex = character[i].charCodeAt(0) - 97;
ans[i] =
psum[queries[i][1]][charIndex] -
psum[queries[i][0] - 1][charIndex];
}
return ans;
}
const s = "baabcaba" ;
const queries = [
[2, 6],
[4, 5],
[1, 6],
[3, 6]
];
const character = [ 'a' , 'a' , 'b' , 'c' ];
const ans = preComputation(s, queries, character);
for (let i = 0; i < ans.length; i++) {
console.log(
`Frequency of character ${character[i]} in the range ${
queries[i][0]
} to ${queries[i][1]} :${ans[i]}`
);
}
|
Time Complexity: O(n+m), where n is length of string and m is number of queries. Auxiliary Space: O(n)
|