Queries to calculate sum of squares of ASCII values of characters of a substring with updates
Given a string S of length N and a 2D array of strings Q[][] consisting of M queries of the following type:
- Query(“U”, “I”, “X”): Update the character at index I with the character X.
- Query(“S”, “L”, “R”): Print the sum of the squares of the position of the characters in the substring {S[L], …., S[R]} according to the English alphabets.
Examples:
Input: S = “geeksforgeeks”, Q[][] = {{“S”, “0”, “2”}, {“S”, “1”, “2”}, {“U”, “1”, “a”}, {“S”, “0”, “2”}, {“S”, “4”, “5”}}
Output:
99
50
75
397
Explanation:
For Query(“S”, “0”, “2”), sum of squares of the positions of the characters in the substring “gee” = 7*7 + 5*5 + 5*5 = 99.
For Query(“S”, “1”, “2”), sum of squares of the positions of the character in the substring “ee” = 5*5 + 5*5 = 50.
For Query(“U”, “1”, “a”): Replacing S[1] by ‘a’ modifies S to “gaeksforgeeks”.
For Query(“S”, “0”, “2”), sum of squares of the positions of the characters in the substring “gae” = 7*7 + 1*1 + 5*5 = 75.
For Query(“S”, “4”, “5”), sum of squares of the positions of the characters in the substring “ks” = 19*19 + 36 = 397Input: S = “geeksforgeeks”, Q[][] = {{“S”, “1”, “2”}}
Output: 50
Naive Approach: The simplest approach to solve the problem is to traverse the array Q[][] for each query and for queries of type ‘S’, simply traverse the substring {S[L], …, S[R]} and print the sum of squares of the positions of the characters in English alphabets. In each iteration to perform the query of type ‘U’, simply assign X to S[I].
Time Complexity: O(N * M)
Auxiliary Space: O(1)
Efficient Approach: The above approach can be optimized by using Segment Tree data structure. Follow the steps below to solve the problem:
- Initialize a segment tree, say tree[], where each node stores the sum of squares of the position of the characters in English alphabets in its subtree.
- For Query(“U”, “I”, “X”), define an update function, similar to the update function of sum range queries. Update S[i] = X and then call the update() function to update the segment tree.
- For Query(“S”, “L”, “R”), define a function query(), similar to sum range query and then, print the sum obtained by calling the query() function for the substring {S[L], …., S[R]}.
Below is the implementation of the above approach:
C++
// C++ implementation of // the above approach #include <bits/stdc++.h> using namespace std; // Structure of a node // of a Segment Tree struct treeNode { int square_sum; }; // Function to construct the Segment Tree void buildTree(string s, treeNode* tree, int start, int end, int treeNode) { // If start and end are equal if (start == end) { // Assign squares of positions // of the characters tree[treeNode].square_sum = pow (s[start] - 'a' + 1, 2); return ; } // Stores the mid value of // the range [start, end] int mid = start + ((end - start) / 2); // Recursive call to left subtree buildTree(s, tree, start, mid, 2 * treeNode); // Recursive call to right subtree buildTree(s, tree, mid + 1, end, 1 + 2 * treeNode); // Update the current node tree[treeNode].square_sum = tree[(2 * treeNode)].square_sum + tree[(2 * treeNode) + 1].square_sum; } // Function to perform the queries of type 2 int querySquareSum(treeNode* tree, int start, int end, int treeNode, int l, int r) { // No overlap if ((l > end) || (r < start)) { return 0; } // If l <= start and r >= end if ((l <= start) && (r >= end)) { // Return the value of treeNode return tree[treeNode].square_sum; } // Calculate middle of the range [start, end] int mid = start + ((end - start) / 2); // Function call to left subtree int X = querySquareSum(tree, start, mid, 2 * treeNode, l, r); // Function call to right subtree int Y = +querySquareSum(tree, mid + 1, end, 1 + 2 * treeNode, l, r); // Return the sum of X and Y return X + Y; } // Function to perform update // queries on a Segment Tree void updateTree(string s, treeNode* tree, int start, int end, int treeNode, int idx, char X) { // If start is equal to end // and idx is equal to start if ((start == end) && (idx == start)) { // Base Case s[idx] = X; tree[treeNode].square_sum = pow (X - 'a' + 1, 2); return ; } // Calculate middle of the range [start, end] int mid = start + ((end - start) / 2); // If idx <= mid if (idx <= mid) { // Function call to left subtree updateTree(s, tree, start, mid, (2 * treeNode), idx, X); } // Otherwise else { // Function call to the right subtree updateTree(s, tree, mid + 1, end, (2 * treeNode) + 1, idx, X); } // Update the current node tree[treeNode].square_sum = tree[(2 * treeNode)].square_sum + tree[(2 * treeNode) + 1].square_sum; } // Function to perform the given queries void PerformQuery(string S, vector<vector<string> > Q) { int n = S.size(); // Stores the segment tree treeNode* tree = new treeNode[(4 * n) + 1]; // Traverse the segment tree for ( int i = 0; i <= (4 * n); i = i + 1) { // Assign 0 to each node tree[i].square_sum = 0; } // Builds segment tree buildTree(S, tree, 0, n - 1, 1); // Traverse the query array Q[][] for ( int i = 0; i < Q.size(); i++) { // If query is of type S if (Q[i][0] == "S" ) { // Stores the left boundary int L = stoi(Q[i][1]); // Stores the right boundary int R = stoi(Q[i][2]); // Prints the sum of squares of the // alphabetic positions of the characters cout << querySquareSum(tree, 0, n - 1, 1, L, R) << endl; } // Otherwise else if (Q[i][0] == "U" ) { // Stores the index of the // character to be updated int I = stoi(Q[i][1]); // Update the segment tree updateTree(S, tree, 0, n - 1, 1, I, Q[i][2][0]); } } } // Driver Code int main() { // Input string S = "geeksforgeeks" ; vector<vector<string> > Q = { { "S" , "0" , "2" }, { "S" , "1" , "2" }, { "U" , "1" , "a" }, { "S" , "0" , "2" }, { "S" , "4" , "5" } }; // Function call PerformQuery(S, Q); } |
Java
// Java implementation of // the above approach import java.io.*; import java.lang.*; import java.util.*; class GFG{ // Structure of a node // of a Segment Tree static class treeNode { int square_sum; treeNode( int square_sum) { this .square_sum = square_sum; } }; // Function to construct the Segment Tree static void buildTree( char s[], treeNode tree[], int start, int end, int treenode) { // If start and end are equal if (start == end) { // Assign squares of positions // of the characters tree[treenode].square_sum = ( int )Math.pow( s[start] - 'a' + 1 , 2 ); return ; } // Stores the mid value of // the range [start, end] int mid = start + ((end - start) / 2 ); // Recursive call to left subtree buildTree(s, tree, start, mid, 2 * treenode); // Recursive call to right subtree buildTree(s, tree, mid + 1 , end, 1 + 2 * treenode); // Update the current node tree[treenode].square_sum = tree[( 2 * treenode)].square_sum + tree[( 2 * treenode) + 1 ].square_sum; } // Function to perform the queries of type 2 static int querySquareSum(treeNode tree[], int start, int end, int treenode, int l, int r) { // No overlap if ((l > end) || (r < start)) { return 0 ; } // If l <= start and r >= end if ((l <= start) && (r >= end)) { // Return the value of treeNode return tree[treenode].square_sum; } // Calculate middle of the range [start, end] int mid = start + ((end - start) / 2 ); // Function call to left subtree int X = querySquareSum(tree, start, mid, 2 * treenode, l, r); // Function call to right subtree int Y = +querySquareSum(tree, mid + 1 , end, 1 + 2 * treenode, l, r); // Return the sum of X and Y return X + Y; } // Function to perform update // queries on a Segment Tree static void updateTree( char s[], treeNode tree[], int start, int end, int treenode, int idx, char X) { // If start is equal to end // and idx is equal to start if ((start == end) && (idx == start)) { // Base Case s[idx] = X; tree[treenode].square_sum = ( int )Math.pow( X - 'a' + 1 , 2 ); return ; } // Calculate middle of the range [start, end] int mid = start + ((end - start) / 2 ); // If idx <= mid if (idx <= mid) { // Function call to left subtree updateTree(s, tree, start, mid, ( 2 * treenode), idx, X); } // Otherwise else { // Function call to the right subtree updateTree(s, tree, mid + 1 , end, ( 2 * treenode) + 1 , idx, X); } // Update the current node tree[treenode].square_sum = tree[( 2 * treenode)].square_sum + tree[( 2 * treenode) + 1 ].square_sum; } // Function to perform the given queries static void PerformQuery(String S, String Q[][]) { int n = S.length(); // Stores the segment tree treeNode tree[] = new treeNode[( 4 * n) + 1 ]; // Traverse the segment tree for ( int i = 0 ; i <= ( 4 * n); i = i + 1 ) { // Assign 0 to each node tree[i] = new treeNode( 0 ); } char s[] = S.toCharArray(); // Builds segment tree buildTree(s, tree, 0 , n - 1 , 1 ); // Traverse the query array Q[][] for ( int i = 0 ; i < Q.length; i++) { // If query is of type S if (Q[i][ 0 ] == "S" ) { // Stores the left boundary int L = Integer.parseInt(Q[i][ 1 ]); // Stores the right boundary int R = Integer.parseInt(Q[i][ 2 ]); // Prints the sum of squares of the // alphabetic positions of the characters System.out.println(querySquareSum( tree, 0 , n - 1 , 1 , L, R)); } // Otherwise else if (Q[i][ 0 ] == "U" ) { // Stores the index of the // character to be updated int I = Integer.parseInt(Q[i][ 1 ]); // Update the segment tree updateTree(s, tree, 0 , n - 1 , 1 , I, Q[i][ 2 ].charAt( 0 )); } } } // Driver Code public static void main(String[] args) { // Input String S = "geeksforgeeks" ; String Q[][] = { { "S" , "0" , "2" }, { "S" , "1" , "2" }, { "U" , "1" , "a" }, { "S" , "0" , "2" }, { "S" , "4" , "5" } }; // Function call PerformQuery(S, Q); } } // This code is contributed by Kingash |
Python3
# Python3 implementation of # the above approach # Structure of a node # of a Segment Tree class treeNode: def __init__( self , x): self .square_sum = x # Function to construct the Segment Tree def buildTree(s, tree, start, end, treeNode): # If start and end are equa if (start = = end): # Assign squares of positions # of the characters tree[treeNode].square_sum = pow ( ord (s[start]) - ord ( 'a' ) + 1 , 2 ) return # Stores the mid value of # the range [start, end] mid = start + ((end - start) / / 2 ) # Recursive call to left subtree buildTree(s, tree, start, mid, 2 * treeNode) # Recursive call to right subtree buildTree(s, tree, mid + 1 , end, 1 + 2 * treeNode) # Update the current node tree[treeNode].square_sum = (tree[( 2 * treeNode)].square_sum + tree[( 2 * treeNode) + 1 ].square_sum) # Function to perform the queries of type 2 def querySquareSum(tree, start, end, treeNode, l, r): # No overlap if ((l > end) or (r < start)): return 0 # If l <= start and r >= end if ((l < = start) and (r > = end)): # Return the value of treeNode return tree[treeNode].square_sum # Calculate middle of the range [start, end] mid = start + ((end - start) / / 2 ) # Function call to left subtree X = querySquareSum(tree, start, mid, 2 * treeNode, l, r) # Function call to right subtree Y = + querySquareSum(tree, mid + 1 , end, 1 + 2 * treeNode, l, r) # Return the sum of X and Y return X + Y # Function to perform update # queries on a Segment Tree def updateTree(s, tree, start, end, treeNode, idx, X): # If start is equal to end # and idx is equal to start if ((start = = end) and (idx = = start)): # Base Case s[idx] = X tree[treeNode].square_sum = pow ( ord (X) - ord ( 'a' ) + 1 , 2 ) return # Calculate middle of the range [start, end] mid = start + ((end - start) / / 2 ) # If idx <= mid if (idx < = mid): # Function call to left subtree updateTree(s, tree, start, mid, ( 2 * treeNode), idx, X) # Otherwise else : # Function call to the right subtree updateTree(s, tree, mid + 1 , end, ( 2 * treeNode) + 1 , idx, X) # Update the current node tree[treeNode].square_sum = (tree[( 2 * treeNode)].square_sum + tree[( 2 * treeNode) + 1 ].square_sum) # Function to perform the given queries def PerformQuery(S, Q): n = len (S) # Stores the segment tree tree = [treeNode( 0 ) for i in range (( 4 * n) + 1 )] # Traverse the segment tree for i in range ( 4 * n + 1 ): # Assign 0 to each node tree[i].square_sum = 0 # Builds segment tree buildTree(S, tree, 0 , n - 1 , 1 ) # Traverse the query array Q[][] for i in range ( len (Q)): # If query is of type S if (Q[i][ 0 ] = = "S" ): # Stores the left boundary L = int (Q[i][ 1 ]) # Stores the right boundary R = int (Q[i][ 2 ]) # Prints the sum of squares of the # alphabetic positions of the characters print (querySquareSum(tree, 0 , n - 1 , 1 , L, R)) # Otherwise elif (Q[i][ 0 ] = = "U" ): # Stores the index of the # character to be updated I = int (Q[i][ 1 ]) # Update the segment tree updateTree(S, tree, 0 , n - 1 , 1 , I, Q[i][ 2 ][ 0 ]) # Driver Code if __name__ = = '__main__' : # Input S = "geeksforgeeks" Q = [ [ "S" , "0" , "2" ], [ "S" , "1" , "2" ], [ "U" , "1" , "a" ], [ "S" , "0" , "2" ], [ "S" , "4" , "5" ] ] # Function call PerformQuery([i for i in S], Q) # This code is contributed by mohit kumar 29 |
C#
using System; public class treeNode { public int square_sum; public treeNode( int square_sum) { this .square_sum = square_sum; } }; public class GFG{ // Function to construct the Segment Tree static void buildTree( char [] s, treeNode[] tree, int start, int end, int treenode) { // If start and end are equal if (start == end) { // Assign squares of positions // of the characters tree[treenode].square_sum = ( int )Math.Pow( s[start] - 'a' + 1, 2); return ; } // Stores the mid value of // the range [start, end] int mid = start + ((end - start) / 2); // Recursive call to left subtree buildTree(s, tree, start, mid, 2 * treenode); // Recursive call to right subtree buildTree(s, tree, mid + 1, end, 1 + 2 * treenode); // Update the current node tree[treenode].square_sum = tree[(2 * treenode)].square_sum + tree[(2 * treenode) + 1].square_sum; } // Function to perform the queries of type 2 static int querySquareSum(treeNode[] tree, int start, int end, int treenode, int l, int r) { // No overlap if ((l > end) || (r < start)) { return 0; } // If l <= start and r >= end if ((l <= start) && (r >= end)) { // Return the value of treeNode return tree[treenode].square_sum; } // Calculate middle of the range [start, end] int mid = start + ((end - start) / 2); // Function call to left subtree int X = querySquareSum(tree, start, mid, 2 * treenode, l, r); // Function call to right subtree int Y = +querySquareSum(tree, mid + 1, end, 1 + 2 * treenode, l, r); // Return the sum of X and Y return X + Y; } // Function to perform update // queries on a Segment Tree static void updateTree( char [] s, treeNode[] tree, int start, int end, int treenode, int idx, char X) { // If start is equal to end // and idx is equal to start if ((start == end) && (idx == start)) { // Base Case s[idx] = X; tree[treenode].square_sum = ( int )Math.Pow( X - 'a' + 1, 2); return ; } // Calculate middle of the range [start, end] int mid = start + ((end - start) / 2); // If idx <= mid if (idx <= mid) { // Function call to left subtree updateTree(s, tree, start, mid, (2 * treenode), idx, X); } // Otherwise else { // Function call to the right subtree updateTree(s, tree, mid + 1, end, (2 * treenode) + 1, idx, X); } // Update the current node tree[treenode].square_sum = tree[(2 * treenode)].square_sum + tree[(2 * treenode) + 1].square_sum; } // Function to perform the given queries static void PerformQuery(String S, String[,] Q) { int n = S.Length; // Stores the segment tree treeNode[] tree = new treeNode[(4 * n) + 1]; // Traverse the segment tree for ( int i = 0; i <= (4 * n); i = i + 1) { // Assign 0 to each node tree[i] = new treeNode(0); } char [] s = S.ToCharArray(); // Builds segment tree buildTree(s, tree, 0, n - 1, 1); // Traverse the query array Q[][] for ( int i = 0; i < Q.GetLength(0); i++) { // If query is of type S if (Q[i,0] == "S" ) { // Stores the left boundary int L = Int32.Parse(Q[i,1]); // Stores the right boundary int R = Int32.Parse(Q[i,2]); // Prints the sum of squares of the // alphabetic positions of the characters Console.WriteLine(querySquareSum( tree, 0, n - 1, 1, L, R)); } // Otherwise else if (Q[i,0] == "U" ) { // Stores the index of the // character to be updated int I = Int32.Parse(Q[i,1]); // Update the segment tree updateTree(s, tree, 0, n - 1, 1, I, Q[i,2][0]); } } } // Driver Code static public void Main (){ // Input string S = "geeksforgeeks" ; string [,] Q = { { "S" , "0" , "2" }, { "S" , "1" , "2" }, { "U" , "1" , "a" }, { "S" , "0" , "2" }, { "S" , "4" , "5" } }; // Function call PerformQuery(S, Q); } } // This code is contributed by unknown2108. |
Javascript
<script> // Javascript implementation of // the above approach // Structure of a node // of a Segment Tree class treeNode { constructor( square_sum) { this .square_sum = square_sum; } } // Function to construct the Segment Tree function buildTree(s,tree,start,end,treenode) { // If start and end are equal if (start == end) { // Assign squares of positions // of the characters tree[treenode].square_sum = Math.pow( s[start].charCodeAt(0) - 'a' .charCodeAt(0) + 1, 2); return ; } // Stores the mid value of // the range [start, end] let mid = start + Math.floor((end - start) / 2); // Recursive call to left subtree buildTree(s, tree, start, mid, 2 * treenode); // Recursive call to right subtree buildTree(s, tree, mid + 1, end, 1 + 2 * treenode); // Update the current node tree[treenode].square_sum = tree[(2 * treenode)].square_sum + tree[(2 * treenode) + 1].square_sum; } // Function to perform the queries of type 2 function querySquareSum(tree,start,end,treenode,l,r) { // No overlap if ((l > end) || (r < start)) { return 0; } // If l <= start and r >= end if ((l <= start) && (r >= end)) { // Return the value of treeNode return tree[treenode].square_sum; } // Calculate middle of the range [start, end] let mid = start + Math.floor((end - start) / 2); // Function call to left subtree let X = querySquareSum(tree, start, mid, 2 * treenode, l, r); // Function call to right subtree let Y = +querySquareSum(tree, mid + 1, end, 1 + 2 * treenode, l, r); // Return the sum of X and Y return X + Y; } // Function to perform update // queries on a Segment Tree function updateTree(s,tree,start,end,treenode,idx,X) { // If start is equal to end // and idx is equal to start if ((start == end) && (idx == start)) { // Base Case s[idx] = X; tree[treenode].square_sum = Math.pow( X.charCodeAt(0) - 'a' .charCodeAt(0) + 1, 2); return ; } // Calculate middle of the range [start, end] let mid = start + Math.floor((end - start) / 2); // If idx <= mid if (idx <= mid) { // Function call to left subtree updateTree(s, tree, start, mid, (2 * treenode), idx, X); } // Otherwise else { // Function call to the right subtree updateTree(s, tree, mid + 1, end, (2 * treenode) + 1, idx, X); } // Update the current node tree[treenode].square_sum = tree[(2 * treenode)].square_sum + tree[(2 * treenode) + 1].square_sum; } // Function to perform the given queries function PerformQuery(S,Q) { let n = S.length; // Stores the segment tree let tree = new Array((4 * n) + 1); // Traverse the segment tree for (let i = 0; i <= (4 * n); i = i + 1) { // Assign 0 to each node tree[i] = new treeNode(0); } let s = S.split( "" ); // Builds segment tree buildTree(s, tree, 0, n - 1, 1); // Traverse the query array Q[][] for (let i = 0; i < Q.length; i++) { // If query is of type S if (Q[i][0] == "S" ) { // Stores the left boundary let L = parseInt(Q[i][1]); // Stores the right boundary let R = parseInt(Q[i][2]); // Prints the sum of squares of the // alphabetic positions of the characters document.write(querySquareSum( tree, 0, n - 1, 1, L, R)+ "<br>" ); } // Otherwise else if (Q[i][0] == "U" ) { // Stores the index of the // character to be updated let I = parseInt(Q[i][1]); // Update the segment tree updateTree(s, tree, 0, n - 1, 1, I, Q[i][2][0]); } } } // Driver Code let S = "geeksforgeeks" ; let Q=[[ "S" , "0" , "2" ], [ "S" , "1" , "2" ], [ "U" , "1" , "a" ], [ "S" , "0" , "2" ], [ "S" , "4" , "5" ]] // Function call PerformQuery(S, Q); // This code is contributed by patel2127 </script> |
99 50 75 397
Time Complexity: O((N + M) * log N)
Auxiliary Space: O(N)
Please Login to comment...