Open In App

Substring segment query & modification

Last Updated : 18 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Given a string S containing only uppercase English letters. You have to process Q queries of the given form:

  • 1 x: Print the maximum size of the segment [i, j] where [Tex]0 ≤ i ≤ x ≤ j < |S| [/Tex]and substring S[i…j] contains only the letter S[x].
  • 2 x: change S[x] = ‘#’ .

Note: For both types of queries, S[x] will not contain the character ‘#’.

Examples:

Input: S = ‘AABBBCCCC’, queries = [[1 0], [2 1], [1 0], [2 2], [1 3]]
Output: 2 1 2
Explanation: Initially S = ‘AABBBCCCC’

  • query[0], longest continuous substring formed by S[0] is ‘AA’ of length 2.
  • query[1], S=’A#BBBCCCC’
  • query[2], longest continuous substring formed by S[0] is ‘A’ of length 1.
  • query[3], S=’A##BBCCCC’
  • query[4], longest continuous substring formed by S[3] is ‘BB’ of length 2.

Input: S = ‘XXYYY’, queries = [[1 3], [2 3], [1 2]]
Output: 3 1
Explanation: Initially S=’XXYYY’

  • query[0], longest continuous substring formed by S[3] is ‘YYY’ of length 3.
  • query[1], S = ‘XXY#Y’
  • query[2], longest continuous substring formed by s[2] is ‘Y’ of length 1.

Approach: To solve the problem follow the below idea:

We can use Binary Search on the ordered ranges formed by the consecutive similar letters, also Set data structure can be used to update those ranges efficiently.

Follow the steps to solve the problem:

  • Create an ordered set of pairs ‘st‘ to store the ranges of consecutive characters.
  • For each query of type 1 x:
    • Use Binary search to find the range [L, R] in which the current index x lies.
    • Simply print the range difference i.e.R-L+1 as the answer to the type 1 query.
  • For each query of type 2 x:
    • Delete the old range [L, R] in which the current index x lies.
    • Insert new ranges i.e. L to x-1 and x+1 to R, into our set ‘st‘.

Below is the implementation of the above algorithm:

C++

#include <bits/stdc++.h> using namespace std; void fn(string s, int n, vector<vector<int> > queries) { // Set of pair to store the ranges set<pair<int, int> > st; // Storing the initial ranges of consecutive characters. for (int i = 0; i < n; i++) { int l = i; while (i + 1 < n && s[i] == s[i + 1]) { i++; } int r = i; st.insert({ l, r }); } // Processint the queries for (auto e : queries) { int type = e[0]; int index = e[1]; // Solving query of type 1 if (type == 1) { // Binary search on the valid range using // upperbound auto it = --st.upper_bound({ index, 1 << 30 }); int left = (*it).first; int right = (*it).second; cout << right - left + 1 << endl; } // Solving the query of type 2 else { // Binary search on the valid range using // upperbound auto it = --st.upper_bound({ index, 1 << 30 }); int left = (*it).first; int right = (*it).second; // Erase the old range st.erase(it); // Inserting the new ranges. if (left != index) st.insert({ left, index - 1 }); if (right != index) st.insert({ index + 1, right }); } } } int main() { string s = "AABBBCCCC"; int n = s.size(); vector<vector<int> > queries = { { 1, 0 }, { 2, 1 }, { 1, 0 }, { 2, 2 }, { 1, 3 } }; fn(s, n, queries); }

Java

// Java Program to Implement above approach import java.util.*; public class Main { static void fn(String s, int n, List<List<Integer>> queries) { // Set of pair to store the ranges TreeSet<Pair<Integer, Integer>> st = new TreeSet<>(); // Storing the initial ranges of consecutive characters. for (int i = 0; i < n; i++) { int l = i; while (i + 1 < n && s.charAt(i) == s.charAt(i + 1)) { i++; } int r = i; st.add(new Pair<>(l, r)); } // Processing the queries for (List<Integer> e : queries) { int type = e.get(0); int index = e.get(1); // Solving query of type 1 if (type == 1) { // Binary search on the valid range using floor Pair<Integer, Integer> queryPair = new Pair<>(index, 1 << 30); Pair<Integer, Integer> it = st.floor(queryPair); int left = it.first; int right = it.second; System.out.println(right - left + 1); } // Solving the query of type 2 else { // Binary search on the valid range using floor Pair<Integer, Integer> queryPair = new Pair<>(index, 1 << 30); Pair<Integer, Integer> it = st.floor(queryPair); int left = it.first; int right = it.second; // Erase the old range st.remove(it); // Inserting the new ranges. if (left != index) st.add(new Pair<>(left, index - 1)); if (right != index) st.add(new Pair<>(index + 1, right)); } } } public static void main(String[] args) { String s = "AABBBCCCC"; int n = s.length(); List<List<Integer>> queries = new ArrayList<>(); queries.add(Arrays.asList(1, 0)); queries.add(Arrays.asList(2, 1)); queries.add(Arrays.asList(1, 0)); queries.add(Arrays.asList(2, 2)); queries.add(Arrays.asList(1, 3)); fn(s, n, queries); } static class Pair<K, V> implements Comparable<Pair<K, V>> { K first; V second; Pair(K first, V second) { this.first = first; this.second = second; } @Override public int compareTo(Pair<K, V> other) { int cmp = ((Comparable<K>) first).compareTo(other.first); return cmp != 0 ? cmp : ((Comparable<V>) second).compareTo(other.second); } } }

C#

// C# Program to Implement above approach using System; using System.Collections.Generic; class Program { static void fn(string s, List<List<int>> queries) { // Set of pairs to store the ranges HashSet<Tuple<int, int>> st = new HashSet<Tuple<int, int>>(); // Storing the initial ranges of consecutive characters. for (int i = 0; i < s.Length; i++) { int l = i; while (i + 1 < s.Length && s[i] == s[i + 1]) { i++; } int r = i; st.Add(new Tuple<int, int>(l, r)); } // Processing the queries foreach (var e in queries) { int type = e[0]; int index = e[1]; // Solving query of type 1 if (type == 1) { // Find the first range with its right bound greater than or equal to the index Tuple<int, int> range = null; foreach (var pair in st) { if (pair.Item2 >= index) { range = pair; break; } } if (range != null) { int left = range.Item1; int right = range.Item2; Console.WriteLine(right - left + 1); } } // Solving the query of type 2 else { // Find the range that contains the index Tuple<int, int> range = null; foreach (var pair in st) { if (pair.Item1 <= index && index <= pair.Item2) { range = pair; break; } } if (range != null) { int left = range.Item1; int right = range.Item2; // Remove the old range st.Remove(range); // Insert the new ranges if (left != index) { st.Add(new Tuple<int, int>(left, index - 1)); } if (right != index) { st.Add(new Tuple<int, int>(index + 1, right)); } } } } } static void Main(string[] args) { string s = "AABBBCCCC"; List<List<int>> queries = new List<List<int>> { new List<int> { 1, 0 }, new List<int> { 2, 1 }, new List<int> { 1, 0 }, new List<int> { 2, 2 }, new List<int> { 1, 3 } }; fn(s, queries); } }

Javascript

class Pair { constructor(first, second) { this.first = first; this.second = second; } compareTo(other) { const cmp = this.first - other.first; return cmp !== 0 ? cmp : this.second - other.second; } } function fn(s, n, queries) { // Set of pairs to store the ranges const st = new Set(); // Storing the initial ranges of consecutive characters for (let i = 0; i < n; i++) { let l = i; while (i + 1 < n && s.charAt(i) === s.charAt(i + 1)) { i++; } let r = i; st.add(new Pair(l, r)); } // Processing the queries for (const e of queries) { const type = e[0]; const index = e[1]; // Solving query of type 1 if (type === 1) { // Binary search on the valid range using floor let left = -1; let right = -1; for (const it of st) { if (it.first <= index && it.second >= index) { left = it.first; right = it.second; break; } } console.log(right - left + 1); } // Solving the query of type 2 else { // Binary search on the valid range using floor let left = -1; let right = -1; for (const it of st) { if (it.first <= index && it.second >= index) { left = it.first; right = it.second; st.delete(it); break; } } // Inserting the new ranges if (left !== index) { st.add(new Pair(left, index - 1)); } if (right !== index) { st.add(new Pair(index + 1, right)); } } } } const s = "AABBBCCCC"; const n = s.length; const queries = [ [1, 0], [2, 1], [1, 0], [2, 2], [1, 3], ]; fn(s, n, queries);

Python3

def fn(s, queries): # List of pairs to store the ranges st = [] # Storing the initial ranges of consecutive characters. n = len(s) i = 0 while i < n: l = i while i + 1 < n and s[i] == s[i + 1]: i += 1 r = i st.append((l, r)) i += 1 # Processing the queries for e in queries: type = e[0] index = e[1] # Solving query of type 1 if type == 1: count = 0 for l, r in st: if l <= index <= r: count += (r - l + 1) print(count) # Solving the query of type 2 else: i = 0 while i < len(st): left, right = st[i] if left <= index <= right: break i += 1 if i < len(st): st.pop(i) if left != index: st.insert(i, (left, index - 1)) if right != index: st.insert(i + 1, (index + 1, right)) if __name__ == "__main__": s = "AABBBCCCC" queries = [ (1, 0), (2, 1), (1, 0), (2, 2), (1, 3) ] fn(s, queries)


Output

2 1 2

Time Complexity: O(N.logN + Q.logN), where N is the string size and Q is the number of queries.
Auxiliary Space: O(N)



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads