📌  相关文章
📜  字符串范围查询以查找等于给定字符串的子集数

📅  最后修改于: 2021-04-17 12:48:18             🧑  作者: Mango

给定长度为N的字符串S,以及以下类型的M个查询:

例子:

天真的方法:

查询类型1:我们将计算查询字符串中每个字符在[L…R]范围内的频率,然后将所有计算出的频率相乘以获得所需的结果。
查询类型2:我们将字符串的第i个字符替换为给定字符。

时间复杂度: 0(m * n)

高效的方法:

  1. 使用细分树,我们可以在log(n)时间内执行这两项操作。段树的每个节点都将包含[L..R]范围内的字符频率。
  2. 函数构建需要n * log(n)时间来创建一个段树,每个节点都包含字符串某个段的字符频率。
  3. 函数get返回一个包含所有字符频率的向量。给定查询字符串的所有频率乘以1e9 + 7模得到所需的结果。
  4. 该函数更新减少了放置在前面的字符的频率,并使出现在段树的节点中的新字符的频率增加了一个。
  5. 函数add_two_result将两个向量相加并返回其结果。

以下是上述方法的C++实现:

CPP
// C++ implementation of the approach
#include
using namespace std;
   
#define N 400005
#define mod 1000000007
   
vector  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  add_two_result(vector  v1,
                                  vector  v2)
{
    vector  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  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  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  v1 = get(2 * pos, l, 
                                    r, st, mid);
    //storing answer of left child n v1
    vector  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  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  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;
   
}


Java
// Java implementation of the approach 
import java.util.*;
  
class GFG {
  
    final static int N = 400005;
    final static int mod = 1000000007;
  
    static int[][] seg_tree = new int[N][26];
    static StringBuilder str;
  
    // A recursive function that constructs
    // Segment Tree for given String
    static 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.charAt(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
    static int[] add_two_result(int[] v1, int[] v2) {
        int[] added_vec = new int[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
    static 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) {
            int[] empty = new int[26];
            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
        int[] v1 = get(2 * pos, l, r, st, mid);
  
        // storing answer of left child n v1
        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
    static 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);
        }
    }
  
    static int count_frequency(String s, int[] v) {
        int ans = 1;
  
        // multiplying frequency of all
        // characters in String hard
        for (int i = 0; i < s.length(); i++)
            ans = (ans * v[s.charAt(i) - 'a']) % mod;
  
        return ans;
    }
  
    // Driver Code
    public static void main(String[] args) {
  
        int n, q, ans;
        int[] v;
  
        n = 15;
        str = new StringBuilder("haardhhardrddrd");
  
        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);
  
        System.out.println(ans);
  
        // Updating character at index 3
        update(1, 3, 1, n, str.charAt(2), 'x');
        str.setCharAt(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);
  
        System.out.println(ans);
    }
}
  
// This code is contributed by
// sanjeev2552


Python3
# Python3 implementation of the approach
N = 400005
mod = 1000000007
  
seg_tree = [[0 for i in range(26)] for i in range(N)]
str = ""
  
# A recursive function that constructs
# Segment Tree for given
def build(pos, st, en):
  
    if (st == en):
  
        # Increment the frequency of character
        # by one if st is equal to en
        seg_tree[pos][ord(str[st - 1] )- ord('a')]+=1
        return
  
    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 i in range(26):
        seg_tree[pos][i] = seg_tree[2 * pos + 1][i] + \
                            seg_tree[2 * pos][i]
  
# A utility function for
# addition of two vectors
def add_two_result(v1,v2):
  
    added_vec=[0]*(26)
  
    # Adding two vector and storing
    # it in another vector
    for i in range(26):
        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
def get(pos, l, r,st, en):
  
    # If segment of this node is
    # outside the given range
    if (l > en or r < st):
        empty = [0]*26
        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 and en <= r):
  
        return seg_tree[pos]
  
    # getting mid of st and en
    mid = st + en >> 1
  
    # storing answer of left child n v1
    v1 = get(2 * pos, l,r, st, mid)
      
    # storing answer of left child n v1
    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
def update(pos, indx, st,en,pre,cur):
  
    # If segment of this node is
    # outside the given range
    if (indx > en or indx < st):
        return
  
    # Subtract frequency of previous character
    seg_tree[pos][ord(pre) - ord('a')]-=1
  
    # Add frequency of new character
    seg_tree[pos][ord(cur) - ord('a')]+=1
  
    if (st != en):
  
        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)
  
def count_frequency(s,v):
  
    ans = 1
      
    # multiplying frequency of all
    # characters in hard
    i = 0
    while i < len(s):
        ans = (ans * v[ord(s[i]) - ord('a')]) % mod
        i += 1
  
    return ans
  
# Driver Code
if __name__ == '__main__':
    v=[]
  
    n = 15
    str = "haardhhardrddrd"
    str=[i for i in str]
  
    build(1, 1, n)
    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)
  
    print(ans)
  
    # 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)
  
    print(ans)
  
# This code is contributed by mohit kumar 29


时间复杂度: 0(m * log(n)+ n * log(n))