Problem:
Score : 500 points

Problem Statement

You are given a string S of length N consisting of lowercase English letters.

Process Q queries of the following two types:

Type 1: change the i_q-th character of S to c_q. (Do nothing if the i_q-th character is already c_q.)

Type 2: answer the number of different characters occurring in the substring of S between the l_q-th and r_q-th characters (inclusive).

Constraints

N, Q, i_q, l_q, and r_q are integers.

S is a string consisting of lowercase English letters.

c_q is a lowercase English letter.

1 \leq N \leq 500000

1 \leq Q \leq 20000

|S| = N

1 \leq i_q \leq N

1 \leq l_q \leq r_q \leq N

There is at least one query of type 2 in each testcase.

Input

Input is given from Standard Input in the following format:

N
S
Q
Query_1
\vdots
Query_Q

Here, Query_i in the 4-th through (Q+3)-th lines is one of the following:

1 i_q c_q

2 l_q r_q

Output

For each query of type 2, print a line containing the answer.

Sample Input 1

7
abcdbbd
6
2 3 6
1 5 z
2 1 1
1 4 a
1 7 d
2 1 7

Sample Output 1

3
1
5

In the first query, cdbb contains three kinds of letters: b , c , and d, so we print 3.

In the second query, S is modified to abcdzbd.

In the third query, a contains one kind of letter: a, so we print 1.

In the fourth query, S is modified to abcazbd.

In the fifth query, S does not change and is still abcazbd.

In the sixth query, abcazbd contains five kinds of letters: a, b, c, d, and z, so we print 5.

Solution in Java8 (OpenJDK 1.8.0) (judge verdict: Wrong Answer)

import java.util.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.StringTokenizer;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
 
public class Main {
    static int maxn = (int) (5e5 + 5);
    static int maxm = (int) (1e6 + 5);
    static String s;
    static char[] a = new char[maxn];
    
    public static class SegTree {
    	int lf, rt;
    	int[] cnt;
    }
    static SegTree[] st = new SegTree[maxn << 2];
    static void PushUp(int x) {
    	for(int i = 0; i < 26; ++i) {
    		st[x].cnt[i] = st[x << 1].cnt[i] + st[x << 1 | 1].cnt[i];
    	}
    }
    static void Build(int x, int lf, int rt) {
    	st[x] = new SegTree();
    	SegTree now = st[x];
    	now.lf = lf;
    	now.rt = rt;
    	now.cnt = new int[26];
    	if(lf == rt) {
    		for(int i = 0; i < 26; ++i) {
        		now.cnt[i] = 0;
        	}
    		++now.cnt[a[lf] - 'a'];
    		return ;
    	}
    	int mid = (lf + rt) >> 1;
    	Build(x << 1, lf, mid);
    	Build(x << 1 | 1, mid + 1, rt);
    	PushUp(x);
    }
    static void Update(int x, int y, int id) {
    	SegTree now = st[x];
    	if(now.lf == now.rt) {
    		int ori = a[y] - 'a';
    		--now.cnt[ori];
    		++now.cnt[id];
    		return ;
    	}
    	int mid = (now.lf + now.rt) >> 1;
    	if(y <= mid) Update(x << 1, y, id);
    	if(y > mid) Update(x << 1 | 1, y ,id);
    	PushUp(x);
    }
    static int[] Query(int x, int lf, int rt) {
    	SegTree now = st[x];
    	int[] ret = new int[26];
    	if(lf <= now.lf && rt >= now.rt) {
    		for(int i = 0; i < 26; ++i) {
    			ret[i] = now.cnt[i];
    		}
    		return ret;
    	}
    	for(int i = 0; i < 26; ++i) {
			ret[i] = 0;
		}
    	int mid = (now.lf + now.rt) >> 1;
    	int[] res;
    	if(lf <= mid) {
    		res = Query(x << 1, lf, rt);
    		for(int i = 0; i < 26; ++i) {
    			ret[i] += res[i];
    		}
    	}
    	if(rt > mid) {
    		res = Query(x << 1 | 1, lf ,rt);
    		for(int i = 0; i < 26; ++i) {
    			ret[i] += res[i];
    		}
    	}
    	return ret;
    }
    public static void main(String[] args) {
        InputStream inputStream = System.in;
        InputReader in = new InputReader(inputStream);
        PrintWriter out = new PrintWriter(System.out);
        
        int n = in.nextInt();
        s = in.next();
        for(int i = 0; i < n; ++i) {
        	a[i + 1] = s.charAt(i);
        }
        int m = in.nextInt();
        Build(1, 1, n);
        for(int i = 1; i <= m; ++i) {
        	int t = in.nextInt();
        	
        	if(t == 1) {
        		int y = in.nextInt();
            	String r = in.next();
            	int id = r.charAt(0) - 'a';
        		Update(1, y, id);
        	} else {
        		int l = in.nextInt();
        		int r = in.nextInt();
        		int[] res = Query(1, l, r);
        		int ans = 0;
        		for(int j = 0; j < 26; ++j) {
        			if(res[j] > 0) {
        				++ans;
        			}
        		}
        		out.println(ans);
        	}
        }
        out.flush();
        // Scanner in = new Scanner(System.in);
        // in.close();
    }
    static class InputReader {
        public BufferedReader reader;
        public StringTokenizer tokenizer;
        public InputReader(InputStream stream) {
            reader = new BufferedReader(new InputStreamReader(stream), 32768);
            tokenizer = null;
        }
        public String next() {
            while(tokenizer == null || !tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return tokenizer.nextToken();
        }
        public int nextInt() {
            return Integer.parseInt(next());
        }
        public long nextLong() {
            return Long.parseLong(next());
        }
    }
}