Skip to content
Related Articles

Related Articles

Improve Article
Queries to calculate sum of squares of ASCII values of characters of a substring with updates
  • Difficulty Level : Expert
  • Last Updated : 26 May, 2021

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 = 397

Input: 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
Output: 
99
50
75
397

 

Time Complexity: O((N + M) * log N)
Auxiliary Space: O(N)

 

Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.  To complete your preparation from learning a language to DS Algo and many more,  please refer Complete Interview Preparation Course.

In case you wish to attend live classes with industry experts, please refer Geeks Classes Live




My Personal Notes arrow_drop_up
Recommended Articles
Page :