String Range Queries to find the number of subsets equal to a given String

Given a string S of length N, and M queries of the following type:

Type 1: 1 L x,
Indicates update Lth index of string S by character ‘x’.

Type 2: 2 L R str
Find the number of subsets in range L to R
which is equal to the string str modulo 1000000007.



Constraints :
|S| <= 100000,
M <= 100000,
1 <= L, R <= |S|,
|str| <= 26,
All characters in str are unique,
S & str consisting of lowercase latin letters.

Examples:

Input : N = 16, M = 3, S = “geeekkksgskeegks”
type = 2: L = 1, R = 7, str = “gek”
type = 1: index = 2, character = ‘x’
type = 2: L = 1, R = 7, str = “gek”
Output : 9 6 4
query2 : No of subsets in string S i range [1…7] that is equal to gek is 9.
query1 : string S is changed to gxeekkksgskeegks after second query.
query2 : No of subsets in string S i range [1…7] that is equal to gek is 6.

Naive Approach :

Query type 1: We will calculate the frequency of each character of the query string in the range [L…R] and then multiply all the calculated frequencies to get the desired result.
Query type 2: We will replace the i’th character of the string with the given character.

Time complexity : 0(m*n)

Efficient Approach :

  1. Using Segment Tree we can perform both operations in log(n) time. Every node of segment tree will contain frequency of characters in range [L..R].
  2. The function build takes n*log(n) time to create a segment tree with every node containing the frequency of characters of some segment of the string.
  3. The function get returns a vector containing frequency of all characters. Multiplication of all frequency of the given query string modulo 1e9+7 gives the desired result.
  4. The function update decreases the frequency of character placed earlier and increases the frequency of new character present in the nodes of the segment tree by one.
  5. The function add_two_result adds two vector and returns their result.

Below is the C++ implementation of the above approach:

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ implementation of the approach
#include<bits/stdc++.h>
using namespace std;
   
#define N 400005
#define mod 1000000007
   
vector <int> seg_tree[N];
string str;
   
// A recursive function that constructs 
// Segment Tree for given string
void build(int pos, int st, int en)
{
   
    if (st == en)
    {
        // Increment the frequency of character
        // by one if st is equal to en
        seg_tree[pos][str[st - 1] - 'a']++;
        return ;
    }
  
    int mid = st + en >> 1;
   
    // Build the segment tree for range st to mid
    build(2 * pos, st, mid);
   
    // Build the segment tree for range mid+1 to en
    build(2 * pos + 1, mid + 1, en);
   
    // It stores addition of frequency of 
    // characters of both of its child after
    // segment tree is build
    for (int i = 0; i < 26; i++)
        seg_tree[pos][i] = seg_tree[2 * pos + 1][i]
                             + seg_tree[2 * pos][i];
   
}
   
// A utility function for 
// addition of two vectors
vector <int> add_two_result(vector <int> v1,
                                  vector <int> v2)
{
    vector <int> added_vec(26);
   
    // Adding two vector and storing
    // it in another vector
    for (int i = 0; i < 26; i++)
        added_vec[i] = v1[i] + v2[i];
   
    // Returning final vector
    return added_vec;
   
}
  
// A recursive function that return vector
// which contains frequency of every character
vector <int> get(int pos, int l, int r,
                               int st, int en)
{
    // If segment of this node is 
    // outside the given range
    if (l > en || r < st)
    {
        vector <int> empty(26, 0);
        return  empty;
    }
   
    // If segment of this node is a part
    // of given range, then return the 
    // frequency every character of the segment
    if (st >= l && en <= r)
    {
        return seg_tree[pos];
    }
   
    // getting mid of st and en
    int mid = st + en >> 1;
   
    //storing answer of left child n v1
    vector <int> v1 = get(2 * pos, l, 
                                    r, st, mid);
    //storing answer of left child n v1
    vector <int> v2 = get(2 * pos + 1, 
                             l, r, mid + 1, en);
   
    //returning the addition of both vectors
    return add_two_result(v1, v2);
   
}
   
// A recursive function that update 
// frequency of new and old character
void update(int pos, int indx, int st,
                    int en, char pre, char cur) {
   
    // If segment of this node is
    // outside the given range
    if (indx > en || indx < st) return;
      
    // Subtract frequency of previous character
    seg_tree[pos][pre - 'a']--; 
   
    // Add frequency of new character
    seg_tree[pos][cur - 'a']++; 
   
    if (st != en)
    {
        int mid = st + en >> 1;
          
        // update left child
        update(2 * pos, indx, st,
                              mid, pre, cur); 
          
        // update right child
        update(2 * pos + 1, indx,
                       mid + 1, en, pre, cur); 
    }
   
}
   
// Utility function to 
// initialize seg_tree vector
void initialize()
{
    for (int i = 0; i < N; i++)
        seg_tree[i].resize(26, 0);
}
   
int count_frequency(string s, vector <int> v)
{
    int ans = 1;
    // multiplying frequency of all 
    // characters in string hard
    for (int i = 0; s[i]; i++)
        ans = (ans * v[s[i] - 'a']) % mod;
   
    return ans;
}
   
// Driver Code
int main()
{
    int n, q, ans;
    vector <int> v;
   
    n = 15;
    str = "haardhhardrddrd";
   
    //initialize and build the seg_tree vector
    initialize();
    build(1, 1, n);
    string s = "hard"
   
    // getting frequency of all 
    // characters from 1 to 5
    v = get(1, 1, 5, 1, n);
   
    // Calling count_frequency
    ans = count_frequency(s, v);
   
    cout << ans << '\n';
   
    // Updating character at index 3
    update(1, 3, 1, n, str[2], 'x');
    str[2] = 'x';
   
    // getting frequency of all 
    // characters from 1 to 5
    v = get(1, 1, 5, 1, n);
   
    //calling count_frequency
    ans = count_frequency(s, v);
   
    cout << ans << '\n';
   
    return 0;
   
}

chevron_right


Time complexity : 0(m*log(n)+n*log(n))



My Personal Notes arrow_drop_up

DTU2K17 BatchSoftware Engineering

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.



Improved By : ManasChhabra2