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-----
7
abcdbbd
6
2 3 6
1 5 z
2 1 1
1 4 a
1 7 d
2 1 7

-----Sample Output-----
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.

Difficulty: interview

Starter code:

Solutions:
n = int(input())
s = list(input())
s = [ord(i)-97 for i in s]

dic = {}
for i in range(26):
    dic[i] = []

for i in range(n):
    dic[s[i]].append(i)

for i in range(26):
    dic[i].append(float('inf'))

from bisect import bisect_left
q = int(input())
for i in range(q):
    x, y, z = input().split()
    if x == '1':
        y, z = int(y) - 1, ord(z) - 97
        p = bisect_left(dic[s[y]], y)
        dic[s[y]].pop(p)
        dic[z].insert(bisect_left(dic[z], y), y)
        s[y] = z
    else:
        res = 0
        y, z = int(y) - 1, int(z) - 1
        for i in range(26):
            p = dic[i][bisect_left(dic[i], y)]
            if p <= z:
                res += 1
        print(res)

import sys
# input = sys.stdin.buffer.readline
def input(): return sys.stdin.readline().rstrip()
sys.setrecursionlimit(10 ** 7)

import bisect
from string import ascii_lowercase
d = dict()
for i, c in enumerate(ascii_lowercase):
    d[c] = i

N = int(input())
S = list(input())
lst = [[] for _ in range(26)]
for i in range(N):
    lst[d[S[i]]].append(i)

Q = int(input())
for q in range(Q):
    a, b, c = input().split()
    if a=='1':
        i = int(b)-1
        if c == S[i]:   # 変更なし
            continue
        idx = bisect.bisect_left(lst[d[S[i]]],i)
        del lst[d[S[i]]][idx]
        bisect.insort_left(lst[d[c]],i)
        S[i] = c
    else:
        l = int(b)-1
        r = int(c)
        ans = 0
        for i in range(26):
            cnt = bisect.bisect_left(lst[i],r) - bisect.bisect_left(lst[i],l)
            if cnt:
                ans += 1
        print(ans)


import sys
input = sys.stdin.readline

def segfunc(x,y):
    return x | y

def init(init_val):
    #set_val
    for i in range(n):
        seg[i+num-1]=init_val[i]
    #built
    for i in range(num-2,-1,-1) :
        seg[i]=segfunc(seg[2*i+1],seg[2*i+2])

def update(k,x):
    k += num-1
    seg[k] = x
    while k:
        k = (k-1)//2
        seg[k] = segfunc(seg[k*2+1],seg[k*2+2])

def query(p,q):
    if q<=p:
        return ide_ele
    p += num-1
    q += num-2
    res=ide_ele
    while q-p>1:
        if p&1 == 0:
            res = segfunc(res,seg[p])
        if q&1 == 1:
            res = segfunc(res,seg[q])
            q -= 1
        p = p//2
        q = (q-1)//2
    if p == q:
        res = segfunc(res,seg[p])
    else:
        res = segfunc(segfunc(res,seg[p]),seg[q])
    return res


n = int(input())
s = input()
q = int(input())

li = []
for i in range(n):
    li.append(1 << ord(s[i]) - ord('a'))

#####単位元######
ide_ele = 0

#num:n以上の最小の2のべき乗
num =2**(n-1).bit_length()
seg=[ide_ele]*2*num
init(li)

for _ in range(q):
    q1 = list(input().split())
    if q1[0] == '1':
        update(int(q1[1]) - 1, 1 << ord(q1[2]) - ord('a'))
    else:
        l,r = int(q1[1]), int(q1[2])
        print((bin(query(l-1,r)).count('1')))


import sys
from bisect import bisect_left, bisect_right, insort

sr = lambda: sys.stdin.readline().rstrip()
ir = lambda: int(sr())
lr = lambda: list(map(int, sr().split()))

N = ir()
S = list('-' + sr())
d = [[] for _ in range(26)]
for i in range(1, N+1):
    s = S[i]
    o = ord(s) - ord('a')
    d[o].append(i)

Q = ir()
for _ in range(Q):
    q, a, b = sr().split()
    if q == '1':
        a = int(a)
        if S[a] == b:
            continue
        prev = ord(S[a]) - ord('a')
        d[prev].pop(bisect_left(d[prev], a))
        next = ord(b) - ord('a')
        insort(d[next], a)
        S[a] = b
    else:
        left = int(a); right = int(b)
        ans = 0
        for alpha in range(26):
            index = bisect_left(d[alpha], left)
            if index < len(d[alpha]) and d[alpha][index] <= right:
                ans += 1
        print(ans)


from bisect import bisect_left, bisect, insort
def main():
    n = int(input())
    s = list(input())
    q = int(input())
    _is = {chr(i):[] for i in range(ord("a"), ord("a")+27)}
    for i, si in enumerate(s):
        _is[si].append(i)
    for _ in range(q):
        t, i, c = input().split()
        i = int(i)-1
        if t == "1":
            if s[i] != c:
                index = bisect_left(_is[s[i]], i)
                del _is[s[i]][index]
                insort(_is[c], i)
                s[i] = c
        else:
            c = int(c) - 1
            cnt = 0
            for _isi in list(_is.values()):
                if _isi:
                    is_in = bisect(_isi, c)-bisect_left(_isi, i)
                    if is_in:
                        cnt += 1
            print(cnt)


def __starting_point():
    main()

__starting_point()

def main():
    import sys
    input = sys.stdin.readline
    from bisect import bisect_left, insort_left
    N = int(input())
    S = list(input())
    Q = int(input())
    dic = {chr(i): [] for i in range(ord('a'), ord('z')+1)}
    for i in range(N):
        dic[S[i]].append(i)
    for _ in range(Q):
        query, a, c = input().split()
        if query == "1":
            i = int(a)-1
            if S[i] == c:
                continue
            ind = bisect_left(dic[S[i]], i)
            dic[S[i]].pop(ind)
            insort_left(dic[c], i)
            S[i] = c
        else:
            l, r = int(a)-1, int(c)-1
            ans = 0
            for inds in dic.values():
                if inds and l <= inds[-1] and inds[bisect_left(inds, l)] <= r:
                    ans += 1
            print(ans)

def __starting_point():
    main()
__starting_point()

import bisect

n = int(input())
s = list(input())
q = int(input())

pos = [[] for i in range(26)]
alp = "abcdefghijklmnopqrstuvwxyz"
atoi = {}
for i in range(26):
    atoi[alp[i]] = i
for i in range(n):
    pos[atoi[s[i]]].append(i)

for i in range(q):
    a, b, c = input().split()
    if a == "1":
        b = int(b)-1
        if c == s[b]:
            continue
        index = bisect.bisect_left(pos[atoi[s[b]]], b)
        del pos[atoi[s[b]]][index]
        bisect.insort_left(pos[atoi[c]], b)
        s[b] = c
    else:
        l, r = int(b)-1, int(c)
        ans = 0
        for i in range(26):
            cnt = bisect.bisect_left(pos[i], r) - bisect.bisect_left(pos[i], l)
            if cnt > 0:
                ans += 1
        print(ans)

import sys
from bisect import bisect_left, bisect_right, insort

sr = lambda: sys.stdin.readline().rstrip()
ir = lambda: int(sr())
lr = lambda: list(map(int, sr().split()))

N = ir()
S = list('-' + sr())
d = [[] for _ in range(26)]
for i in range(1, N+1):
    s = S[i]
    o = ord(s) - ord('a')
    insort(d[o], i)

Q = ir()
for _ in range(Q):
    q, a, b = sr().split()
    if q == '1':
        a = int(a)
        if S[a] == b:
            continue
        prev = ord(S[a]) - ord('a')
        d[prev].pop(bisect_left(d[prev], a))
        next = ord(b) - ord('a')
        insort(d[next], a)
        S[a] = b
    else:
        a = int(a); b = int(b)
        ans = 0
        for alpha in range(26):
            if bisect_right(d[alpha], b) - bisect_left(d[alpha], a) >= 1:
                ans += 1
        print(ans)


# -*- coding: utf-8 -*-

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

# from helper import elapsed_time
from bisect import bisect_left, insort_left


N = int(input())
s = list(input())
Q = int(input())

# a-zまでの26文字に対して、s文字列の各文字に該当するindexを格納しておく
d = {i: [] for i in range(26)}
for i, c in enumerate(s):
    d[ord(c) - ord("a")].append(i)


# @elapsed_time
def convert(d: dict, s: list, q1: int, q2: str):
    if s[q1] == q2:
        return d, s

    didx_before = ord(s[q1]) - ord("a")
    didx_after = ord(q2) - ord("a")

    idx = bisect_left(d[didx_before], q1)
    d[didx_before].pop(idx)
    s[q1] = q2
    insort_left(d[didx_after], q1)

    return d, s


# @elapsed_time
def calc(d: dict, q1: int, q2: int):
    ans = 0
    for i in range(26):
        idx = bisect_left(d[i], q1)
        if d[i] and q1 <= d[i][-1] and d[i][idx] < q2 + 1:
            ans += 1

    return ans


for _ in range(Q):
    q, q1, q2 = input().split()
    q = int(q)
    if q == 1:
        d, s = convert(d, s, int(q1) - 1, q2)
    if q == 2:
        print((calc(d, int(q1) - 1, int(q2) - 1)))


import sys

input = lambda: sys.stdin.readline().rstrip()


class SegTree:

    def __init__(self, init_val, segfunc, ide_ele):

        n = len(init_val)
        self.ide_ele = ide_ele
        self.segfunc = segfunc
        self.num = 2**(n - 1).bit_length()
        self.seg = [self.ide_ele] * 2 * self.num

        for i in range(n):
            self.seg[i + self.num - 1] = init_val[i]

        for i in range(self.num - 2, -1, -1):
            self.seg[i] = self.segfunc(self.seg[2 * i + 1], self.seg[2 * i + 2])

    def update(self, k, x):
        k += self.num - 1
        self.seg[k] = x
        while k:
            k = (k - 1) // 2
            self.seg[k] = self.segfunc(self.seg[k * 2 + 1], self.seg[k * 2 + 2])

    def query(self, p, q):
        if q <= p:
            return self.ide_ele
        p += self.num - 1
        q += self.num - 2
        res = self.ide_ele
        while q - p > 1:
            if p & 1 == 0:
                res = self.segfunc(res, self.seg[p])
            if q & 1 == 1:
                res = self.segfunc(res, self.seg[q])
                q -= 1
            p = p // 2
            q = (q - 1) // 2
        if p == q:
            res = self.segfunc(res, self.seg[p])
        else:
            res = self.segfunc(self.segfunc(res, self.seg[p]), self.seg[q])
        return res


def solve():

    N = int(input())
    S = list(input())
    Q = int(input())
    seg = [1 << (ord(s) - ord('a')) for s in S]

    segtree = []
    segfunc = lambda a, b: a | b

    segtree = SegTree(seg, segfunc, 0)

    ans = []
    for i in range(Q):
        a, b, c = input().split()
        a = int(a)

        if a == 1:
            b = int(b) - 1
            al = ord(c) - ord('a')
            segtree.update(b, 1 << al)
        elif a == 2:
            b, c = int(b) - 1, int(c)
            res = segtree.query(b, c)
            res = sum(list(map(int, bin(res)[2:])))
            ans.append(res)

    print(('\n'.join(map(str, ans))))


def __starting_point():
    solve()

__starting_point()

f=input; from bisect import bisect_left as g, insort as h
d={chr(97+i):[] for i in range(26)}
f(); s,i=list(f()),0
for c in s: d[c]+=[i]; i+=1
for _ in range(int(f())):
  a,b,c=f().split(); b=int(b)-1
  if a>'1': print(sum(1 for l in d.values() if g(l,b)<len(l) and l[g(l,b)]<int(c)))
  elif s[b]!=c: l=d[s[b]]; l.pop(g(l,b)); s[b]=c; h(d[c],b)

from bisect import bisect_left, bisect_right, insort_left
import sys
readlines = sys.stdin.readline
def main():

	n = int(input())
	s = list(input())
	q = int(input())

	d = {}
	flag = {}
	for i in list('abcdefghijklmnopqrstuvwxyz'):
		d.setdefault(i, []).append(-1)
		flag.setdefault(i, []).append(-1)
	for i in range(n):
		d.setdefault(s[i], []).append(i)
	for i in range(q):
		q1,q2,q3 = map(str,input().split())
		if q1 == '1':
			q2 = int(q2) - 1
			if s[q2] != q3:
				insort_left(flag[s[q2]],q2)
				insort_left(d[q3],q2)
				s[q2] = q3
		else:
			ans = 0
			q2 = int(q2) - 1
			q3 = int(q3) - 1
			if q2 == q3:
				print(1)
				continue
			for string,l in d.items():
				res = 0
				if d[string] != [-1]:
					left = bisect_left(l,q2)
					right = bisect_right(l,q3)
				else:
					left = 0
					right = 0
				if string in flag:
					left2 = bisect_left(flag[string],q2)
					right2 = bisect_right(flag[string],q3)
				else:
					left2 = 0
					right2 = 0
				if left != right:
					if right - left > right2 - left2:
						res = 1
				ans += res
				#print(string,l,res)
			print(ans)
def __starting_point():
	main()
__starting_point()

# https://atcoder.jp/contests/abc157/tasks/abc157_e
# セグメント木
# 各文字をビットに対応させる(セグ木を26本持っても良い)
import sys
read = sys.stdin.readline


def read_a_int():
    return int(read())


class SegmentTree:
    def __init__(self, ls: list, segfunc, identity_element):
        '''
        セグ木 (下書き)
        一次元のリストlsを受け取り初期化する。O(len(ls))
        区間のルールはsegfuncによって定義される
        identity elementは単位元。e.g., 最小値を求めたい→inf, 和→0, 積→1, gcd→0
        [単位元](https://ja.wikipedia.org/wiki/%E5%8D%98%E4%BD%8D%E5%85%83)
        '''
        self.ide = identity_element
        self.func = segfunc
        n = len(ls)
        self.num = 2 ** (n - 1).bit_length()  # n以上の最小の2のべき乗
        self.tree = [self.ide] * (2 * self.num - 1)  # −1はぴったりに作るためだけど気にしないでいい
        for i, l in enumerate(ls):  # 木の葉に代入
            self.tree[i + self.num - 1] = l
        for i in range(self.num - 2, -1, -1):  # 子を束ねて親を更新
            self.tree[i] = segfunc(self.tree[2 * i + 1], self.tree[2 * i + 2])

    def update(self, i, x):
        '''
        i番目の要素をxに変更する(木の中間ノードも更新する) O(logN)
        '''
        i += self.num - 1
        self.tree[i] = x
        while i:  # 木を更新
            i = (i - 1) // 2
            self.tree[i] = self.func(self.tree[i * 2 + 1],
                                     self.tree[i * 2 + 2])

    def query(self, l, r):
        '''
        区間[l,r)に対するクエリをO(logN)で処理する。例えばその区間の最小値、最大値、gcdなど
        '''
        if r <= l:
            return ValueError('invalid index (l,rがありえないよ)')
        l += self.num
        r += self.num
        res = self.ide
        while l<r:  # 右から寄りながら結果を結合していくイメージ
            if l & 1:
                res = self.func(res, self.tree[l-1])
                l+=1
            if r & 1:
                r -= 1
                res = self.func(res, self.tree[r-1])
            l >>=1
            r >>=1
        return res


def segfunc(x, y):
    # 処理したい内容
    return x | y


def moji_to_bit(a):
    return 1 << (ord(a) - ord('a'))


def bit_to_sum(n):
    return sum([(n >> i) & 1 for i in range(n.bit_length())])


N = read_a_int()
S = read()[:-1]
S_bit = [moji_to_bit(s) for s in S]

# build segment tree
st = SegmentTree(S_bit, segfunc, 0)

Q = read_a_int()
for q in range(Q):
    com, a, b = read().split()
    if int(com) == 1:
        i, c = int(a) - 1, b
        st.update(i, moji_to_bit(c))
    else:
        l, r = int(a) - 1, int(b)
        print((bit_to_sum(st.query(l, r))))


class SegmentTree():
    '''
    非再帰
    segment tree
    '''

    def __init__(self, n, func, init=float('inf')):
        '''
        n->配列の長さ
        func:func(a,b)->val,　func=minだとRMQになる
        木の高さhとすると,
        n:h-1までのノード数。h段目のノードにアクセスするために使う。
        data:ノード。
        parent:k->child k*2+1とk*2+2
        '''
        self.n = 2**(n-1).bit_length()
        self.init = init
        self.data = [init]*(2*self.n)
        self.func = func

    def set(self, k, v):
        '''
        あたいの初期化
        '''
        self.data[k+self.n-1] = v

    def build(self):
        '''
        setの後に一斉更新
        '''
        for k in reversed(list(range(self.n-1))):
            self.data[k] = self.func(self.data[k*2+1], self.data[k*2+2])

    def update(self, k, a):
        '''
        list[k]=aに更新する。
        更新ぶんをrootまで更新
        '''
        k += self.n-1
        self.data[k] = a

        while k > 0:
            k = (k-1)//2
            self.data[k] = self.func(self.data[k*2+1], self.data[k*2+2])

    def query(self, l, r):
        '''
        [l,r)のfuncを求める
        '''
        L = l+self.n
        R = r+self.n
        ret = self.init
        while L < R:
            if R & 1:
                R -= 1
                ret = self.func(ret, self.data[R-1])
            if L & 1:
                ret = self.func(ret, self.data[L-1])
                L += 1
            L >>= 1
            R >>= 1
        return ret


N = int(input())
S = input()
Q = int(input())
queries = [list(input().split()) for _ in range(Q)]


def a2n(a):
    return ord(a)-ord("a")


def createInp(a):
    return 1 << a2n(a)


Seg = SegmentTree(len(S), lambda x, y: x | y, init=0)
for i, s in enumerate(S):
    Seg.set(i, createInp(s))
Seg.build()

for q, l, r in queries:
    if q == "1":
        Seg.update(int(l)-1, createInp(r))
    else:
        print((bin(Seg.query(int(l)-1, int(r))).count('1')))


from math import log2, ceil


class SegTree:
    #####単位元######
    ide_ele = 0

    def __init__(self, init_val):
        n = len(init_val)
        self.num = 2**ceil(log2(n))
        self.seg = [self.ide_ele] * (2 * self.num - 1)

        for i in range(n):
            self.seg[i + self.num - 1] = init_val[i]

        # built
        for i in range(self.num - 2, -1, -1):
            self.seg[i] = self.segfunc(self.seg[2 * i + 1], self.seg[2 * i + 2])

    #####segfunc######
    def segfunc(self, x, y):
        return x|y

    def update(self, k, x):
        k += self.num - 1
        self.seg[k] = x
        while k:
            k = (k - 1) // 2
            self.seg[k] = self.segfunc(self.seg[k * 2 + 1], self.seg[k * 2 + 2])

    def query(self, a, b, k, l, r):
        if r <= a or b <= l:
            return self.ide_ele
        if a <= l and r <= b:
            return self.seg[k]
        else:
            vl = self.query(a, b, k*2+1, l, (l+r)//2)
            vr = self.query(a, b, k*2+2, (l+r)//2, r)
            return self.segfunc(vl, vr)


N = int(input())
S = input()
Q = int(input())

li = [1 << (ord(s) - ord('a')) for s in S]
seg_tree = SegTree(li)
ans = []
for _ in range(Q):
    i, l, r = input().split()
    i = int(i)
    l = int(l)
    if i == 1:
        seg_tree.update(l-1, 1 << (ord(r) - ord('a')))
    else:
        r = int(r)
        num = seg_tree.query(l-1, r, 0, 0, seg_tree.num)
        ans.append(bin(num).count('1'))

for a in ans:
    print(a)


#!/usr/bin/env python3
import bisect

# Generated by 1.1.6 https://github.com/kyuridenamida/atcoder-tools  (tips: You use the default template now. You can remove this line by using your custom template)
def main():
    N = int(input())
    S = list(input())
    Q = int(input())

    L = [[] for x in range(26)] # i番目のリストは、アルファベットが現れるインデックスのリストを持つ。

    for idx, s in enumerate(S):
        L[ord(s)-ord("a")].append(idx)

    for _ in range(Q):
        row = input().split()
        if row[0] == '1':
            y, z = int(row[1])-1, row[2]
            if S[y] != z:
                idx_target = ord(S[y]) - ord("a")
                idx_del = bisect.bisect_left(L[idx_target], y)
                if idx_del < len(L[idx_target]):
                    L[idx_target].pop(idx_del)
                bisect.insort(L[ord(z) - ord("a")], y)
                S[y] = z

        elif row[0] == '2':
            y, z = int(row[1]) - 1, int(row[2]) - 1
            answer = 0
            for idx_alphabet in range(26):
                left = bisect.bisect_left(L[idx_alphabet], y)
                if left < len(L[idx_alphabet]) and y <= L[idx_alphabet][left] <= z:
                    answer += 1
            print(answer)

    # Failed to predict input format
    pass

def __starting_point():
    main()

__starting_point()

from bisect import bisect_left

n = int(input())
s = list(input())
q = int(input())
query = [input() for _ in range(q)]

p = [[] for _ in range(26)]

for i, x in enumerate(s):
    p[ord(x) - ord('a')].append(i)

for x in query:
    a, b, c = x.split()
    #print(c)
    if a == '1':
        b = int(b) - 1
        if s[b] == c:
            continue
        d = ord(s[b]) - ord('a')
        e = ord(c) - ord('a')
        temp = bisect_left(p[d], b)
        p[d].pop(temp)
        temp = bisect_left(p[e], b)
        p[e].insert(temp, b)
        s[b] = c
    else:
        b = int(b) - 1
        c = int(c) - 1
        #print(b, c)
        ans = 0
        for i in range(26):
            L = bisect_left(p[i], b)
            if L >= len(p[i]):
                continue
            if p[i][L] <= c:
                ans += 1
        print(ans)


class SegmentTree:
    def __init__(self,n,ele=0):
        self.ide_ele = ele
        self.num =2**(n-1).bit_length()
        self.seg=[self.ide_ele]*2*self.num

    def segfunc(self,x,y):
        return x|y

    def init(self,init_val):
        for i in range(len(init_val)):
            self.seg[i+self.num-1]=init_val[i]
        for i in range(self.num-2,-1,-1) :
            self.seg[i]=self.segfunc(self.seg[2*i+1],self.seg[2*i+2]) 

    def update(self,k,x):
        k += self.num-1
        if self.seg[k]==x:
          return 0
        self.seg[k] = x
        while k:
            k = (k-1)//2
            self.seg[k] = self.segfunc(self.seg[k*2+1],self.seg[k*2+2])

    def query(self,p,q):
        if q<=p:
            return self.ide_ele
        p += self.num-1
        q += self.num-2
        res=self.ide_ele
        while q-p>1:
            if p&1 == 0:
                res = self.segfunc(res,self.seg[p])
            if q&1 == 1:
                res = self.segfunc(res,self.seg[q])
                q -= 1
            p = p//2
            q = (q-1)//2
        if p == q:
            res = self.segfunc(res,self.seg[p])
        else:
            res = self.segfunc(self.segfunc(res,self.seg[p]),self.seg[q])
        return res
      
N,S,Q=int(input()),input(),int(input())
d={'a': 1, 'b': 2, 'c': 4, 'd': 8, 'e': 16, 'f': 32, 'g': 64, 'h': 128, 'i': 256, 'j': 512, 'k': 1024, 'l': 2048, 'm': 4096, 'n': 8192, 'o': 16384, 'p': 32768, 'q': 65536, 'r': 131072, 's': 262144, 't': 524288, 'u': 1048576, 'v': 2097152, 'w': 4194304, 'x': 8388608, 'y': 16777216, 'z': 33554432}

initial=[d[c] for c in list(S)]

ST=SegmentTree(N)
ST.init(initial)

for _ in range(Q):
    typ,a,b=input().split()
    if typ=='1':
        i,c=int(a),d[b]
        ST.update(i-1,c)
    else:
        l,r=int(a)-1,int(b)
        x=bin(ST.query(l,r))[2:]
        print(sum(map(int,list(x))))

# AtCoder Beginner Contest 157
# E - Simple String Queries
# https://atcoder.jp/contests/abc157/tasks/abc157_e
from bisect import bisect_left, insort_left
import sys
input = sys.stdin.readline
OFS = ord('a')
_ = input()
S = list(input()[:-1])

D = [[] for _ in [0]*26]
for i, s in enumerate(S):
    D[ord(s)-OFS].append(i)

for _ in [0]*int(input()):
    Q = input()[:-1].split()
    if Q[0] == '1':
        i, c = int(Q[1])-1, Q[2]

        if S[i] != c:
            s = ord(S[i])-OFS
            D[s].pop(bisect_left(D[s], i))
            insort_left(D[ord(c)-OFS], i)
            S[i] = c

    else:
        l, r = int(Q[1])-1, int(Q[2])-1

        cnt = 0
        for L in D:
            i = bisect_left(L, l)
            if i < len(L) and L[i] <= r:
                cnt += 1

        print(cnt)


#!python3

import sys

iim = lambda: map(int, sys.stdin.readline().rstrip().split())

def resolve():
    N = int(input())
    S = list(input())
    Q = int(input())

    c0 = ord('a')
    smap = [1<<(i-c0) for i in range(c0, ord('z')+1)]

    T = [0]*N + [smap[ord(S[i])-c0] for i in range(N)]

    for i in range(N-1, 0, -1):
        i2 = i << 1
        T[i] = T[i2] | T[i2|1]

    ans = []
    #print(T)
    for cmd, i, j in zip(*[iter(sys.stdin.read().split())]*3):
        i = int(i) - 1
        if cmd == "1":
            if S[i] == j:
                continue

            S[i] = j
            i0 = N + i
            T[i0] = smap[ord(j)-c0]
            while i0 > 1:
                i0 = i0 >> 1
                T[i0] = T[i0+i0] | T[i0-~i0]
        elif cmd == "2":
            i += N
            j = int(j) + N

            d1 = 0
            while i < j:
                if i & 1:
                    d1 |= T[i]
                    i += 1
                if j & 1:
                    j -= 1
                    d1 |= T[j]

                i >>= 1; j >>=1

            ans.append(bin(d1).count('1'))

    print(*ans, sep="\n")

def __starting_point():
    resolve()

__starting_point()

import bisect
from collections import defaultdict

N = int(input())
S = list(input())
Q = int(input())

alpha_dic = defaultdict(list)

for i, s in enumerate(S):
    alpha_dic[s].append(i)

for _ in range(Q):
    l, m, n = input().split()

    if l == '1':
        m = int(m) - 1
        c = S[m]

        if c == n:
            continue
        else:
            i = bisect.bisect_left(alpha_dic[c], m)
            del alpha_dic[c][i]

            bisect.insort_left(alpha_dic[n], m)
            S[m] = n
    else:
        m, n = int(m) - 1, int(n) - 1

        cnt = 0
        for _, val in list(alpha_dic.items()):
            if not val:
                continue

            left_idx = bisect.bisect_left(val, m)
            right_idx = bisect.bisect_left(val, n)
            l = len(val)

            if left_idx > right_idx or (left_idx == right_idx and left_idx == l) or (left_idx == right_idx and val[left_idx] != m and val[right_idx] != n):
                continue

            cnt += 1

        print(cnt)


import sys
input = sys.stdin.readline

class SegTree:
    def __init__(self, size, f=lambda x,y: x|y, e=0):
        self.size = 2**(size-1).bit_length()
        self.e = e
        self.tree = [e] * (self.size*2)
        self.f = f
    
    def init_tree(self, l):
        for i in range(len(l)):
            self.tree[i+self.size] = l[i]
        for i in range(self.size - 1, -1, -1):
            self.tree[i] = self.f(self.tree[i*2], self.tree[i*2+1])
    
    def update(self, i, v):
        i += self.size
        self.tree[i] = v
        while i > 0:
            i >>= 1
            self.tree[i] = self.f(self.tree[i*2], self.tree[i*2+1])
    
    def query(self, l, r):
        l += self.size
        r += self.size
        lval = self.e
        rval = self.e
        while l < r:
            if l & 1:
                lval = self.f(lval, self.tree[l])
                l += 1
            if r & 1:
                r -= 1
                rval = self.f(rval, self.tree[r])
            l >>= 1
            r >>= 1
        return self.f(lval, rval)

def a2i(c):
    return 1<<(ord(c) - ord("a"))

def main():
    N = int(input())
    S = [a2i(s) for s in input().strip()]
    Q = int(input())

    tree = SegTree(N+5)
    tree.init_tree(S)

    for _ in range(Q):
        t, x, y = input().split()
        if t == "1":
            x = int(x) - 1
            y = 1<<(ord(y) - ord("a"))
            tree.update(x, y)
        else:
            x = int(x) - 1
            y = int(y)
            tmp = tree.query(x, y)
            ans = 0
            while tmp > 0:
                ans += tmp & 1
                tmp >>= 1
            print(ans)

def __starting_point():
    main()
__starting_point()

#!/usr/bin python3
# -*- coding: utf-8 -*-

from bisect import bisect_left, bisect_right


n = int(input())
s = list(input())
q = int(input())

al = 'abcdefghijklmnopqrstuvwxyz'
dicq = {i:[] for i in al}
for i, si in enumerate(s):
    dicq[si].append(i)

def q1(iq, cq):
    #print(iq,cq,s[iq],dicq[s[iq]],bisect_left(dicq[s[iq]], iq))
    dicq[s[iq]].pop(bisect_left(dicq[s[iq]], iq))   #更新前から削除
    s[iq] = cq
    dicq[cq].insert(bisect_left(dicq[cq], iq), iq)   #更新後に追加

def q2(l, r):
    ret = 0
    for ai in al:
        if len(dicq[ai])==0: continue
        li = bisect_left(dicq[ai], l)
        ri = bisect_right(dicq[ai], r)
        if li != ri:
            ret += 1
        elif li == len(dicq[ai]): continue
        elif dicq[ai][li] == l:
            ret += 1
    #    print(l,r,ai,dicq[ai],li,ri,ret)
    print(ret)

for i in range(q):
    fg, x, y = input().split()
    if fg == '1':
        q1(int(x)-1, y)
    else:
        q2(int(x)-1, int(y)-1)


import sys
input = sys.stdin.readline

def solve():
    ordA = ord('a')

    N = int(input())
    Ss = input().rstrip()
    Q = int(input())
    querys = [tuple(input().split()) for _ in range(Q)]

    idEle = 0
    def _binOpe(x, y):
        return x | y
    def makeSegTree(numEle):
        numPow2 = 2 ** (numEle-1).bit_length()
        data = [idEle] * (2*numPow2)
        return data, numPow2
    def setInit(As):
        for iST, A in enumerate(As, numPow2):
            data[iST] = A
        for iST in reversed(list(range(1, numPow2))):
            data[iST] = _binOpe(data[2*iST], data[2*iST+1])
    def _update1(iA, A):
        iST = iA + numPow2
        data[iST] = A
    def update(iA, A):
        _update1(iA, A)
        iST = iA + numPow2
        while iST > 1:
            iST >>= 1
            data[iST] = _binOpe(data[2*iST], data[2*iST+1])
    def getValue(iSt, iEn):
        L = iSt + numPow2
        R = iEn + numPow2
        ans = idEle
        while L < R:
            if L & 1:
                ans = _binOpe(ans, data[L])
                L += 1
            if R & 1:
                R -= 1
                ans = _binOpe(ans, data[R])
            L >>= 1
            R >>= 1
        return ans

    data, numPow2 = makeSegTree(N)
    As = [(1<<(ord(S)-ordA)) for S in Ss]
    setInit(As)

    anss = []
    for tp, *vs in querys:
        if tp == '1':
            i, c = vs
            i = int(i)
            update(i-1, 1<<(ord(c)-ordA))
        else:
            L, R = vs
            L, R = int(L), int(R)
            v = getValue(L-1, R)
            anss.append(bin(v).count('1'))

    print(('\n'.join(map(str, anss))))


solve()


from bisect import bisect_left,bisect_right
import string 

dic = {c:[] for c in string.ascii_lowercase}

n = int(input())
s = list(input())
q = int(input())

for i,c in enumerate(s,start=1):
  dic[c].append(i)
  
for i in range(q):
  p,l,r = map(str,input().split())
  if p =='1':
    j = int(l)
    if r == s[j-1]:
      continue
    c = s[j-1]    
    dic[c].pop(bisect_left(dic[c],j))
    dic[r].insert(bisect_left(dic[r],j),j)
    s[j-1] = r
  else:
    ans = 0
    l = int(l)
    r = int(r)
    for c in string.ascii_lowercase:
      pl = bisect_left(dic[c],l)
      if pl < len(dic[c]):
        if dic[c][pl] <= r:
          ans += 1
    print(ans)

import sys
def input(): return sys.stdin.readline().rstrip()

# ライブラリ参照https://atcoder.jp/contests/practice2/submissions/16580070
class SegmentTree:

    __slots__ = ["func", "e", "original_size", "n", "data"]

    def __init__(self, length_or_list, func, e):
        self.func = func
        self.e = e
        if isinstance(length_or_list, int):
            self.original_size = length_or_list
            self.n = 1 << ((length_or_list - 1).bit_length())
            self.data = [self.e] * self.n
        else:
            self.original_size = len(length_or_list)
            self.n = 1 << ((self.original_size - 1).bit_length())
            self.data = [self.e] * self.n + length_or_list + \
                [self.e] * (self.n - self.original_size)
            for i in range(self.n-1, 0, -1):
                self.data[i] = self.func(self.data[2*i], self.data[2*i+1])

    def replace(self, index, value):
        index += self.n
        self.data[index] = value
        index //= 2
        while index > 0:
            self.data[index] = self.func(
                self.data[2*index], self.data[2*index+1])
            index //= 2

    def folded(self, l, r):
        left_folded = self.e
        right_folded = self.e
        l += self.n
        r += self.n
        while l < r:
            if l % 2:
                left_folded = self.func(left_folded, self.data[l])
                l += 1
            if r % 2:
                r -= 1
                right_folded = self.func(self.data[r], right_folded)
            l //= 2
            r //= 2
        return self.func(left_folded, right_folded)

    def all_folded(self):
        return self.data[1]

    def __getitem__(self, index):
        return self.data[self.n + index]

    def max_right(self, l, f):
        # assert f(self.e)
        if l >= self.original_size:
            return self.original_size
        l += self.n
        left_folded = self.e
        while True:
            # l //= l & -l
            while l % 2 == 0:
                l //= 2
            if not f(self.func(left_folded, self.data[l])):
                while l < self.n:
                    l *= 2
                    if f(self.func(left_folded, self.data[l])):
                        left_folded = self.func(left_folded, self.data[l])
                        l += 1
                return l - self.n
            left_folded = self.func(left_folded, self.data[l])
            l += 1
            if l == l & -l:
                break
        return self.original_size

    # 未verify
    def min_left(self, r, f):
        # assert f(self.e)
        if r == 0:
            return 0
        r += self.n
        right_folded = self.e
        while True:
            r //= r & -r
            if not f(self.func(self.data[r], right_folded)):
                while r < self.n:
                    r = 2 * r + 1
                    if f(self.func(self.data[r], right_folded)):
                        right_folded = self.func(self.data[r], right_folded)
                        r -= 1
                return r + 1 - self.n
            if r == r & -r:
                break
        return 0


def orr(x, y):
    return x | y


def main():
    N = int(input())
    S = input()
    S = list(map(lambda c: 2**(ord(c) - ord('a')), list(S)))
    Q = int(input())
    seg = SegmentTree(S, orr, 0)
    for _ in range(Q):
        num, x, y = input().split()
        if num == '1':
            seg.replace(int(x)-1, 2**(ord(y) - ord('a')))
        else:
            bits = seg.folded(int(x)-1, int(y))
            print(sum(map(int, list(bin(bits))[2:])))


def __starting_point():
    main()
__starting_point()

from bisect import bisect_left, bisect_right, insort_left

n = int(input())
s = list(input())
q = int(input())

d = {}
flag = {}
for i in list('abcdefghijklmnopqrstuvwxyz'):
	d.setdefault(i, []).append(-1)
	flag.setdefault(i, []).append(-1)
for i in range(n):
	d.setdefault(s[i], []).append(i)
for i in range(q):
	q1,q2,q3 = list(map(str,input().split()))
	if q1 == '1':
		q2 = int(q2) - 1
		if s[q2] != q3:
			insort_left(flag[s[q2]],q2)
			insort_left(d[q3],q2)
			s[q2] = q3
	else:
		ans = 0
		q2 = int(q2) - 1
		q3 = int(q3) - 1
		if q2 == q3:
			print((1))
			continue
		for string,l in list(d.items()):
			res = 0
			if d[string] != [-1]:
				left = bisect_left(l,q2)
				right = bisect_right(l,q3)
			else:
				left = 0
				right = 0
			if string in flag:
				left2 = bisect_left(flag[string],q2)
				right2 = bisect_right(flag[string],q3)
			else:
				left2 = 0
				right2 = 0
			if left != right:
				if right - left > right2 - left2:
					res = 1
			ans += res
			#print(string,l,res)
		print(ans)


#!python3

import sys

iim = lambda: map(int, input().rstrip().split())

def popcnt2(n):
    a = (
        0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
    )
    ans = 0
    while n:
        ans += a[n&0xff]
        n >>= 8
    return ans

def resolve():
    N = int(input())
    S = list(input())
    Q = int(input())

    c0 = ord('a')
    smap = [1<<(i-c0) for i in range(c0, ord('z')+1)]

    T = [0]*N + [smap[ord(S[i])-c0] for i in range(N)]

    for i in range(N-1, 0, -1):
        i2 = i << 1
        T[i] = T[i2] | T[i2|1]

    ans = []
    #print(T)
    for cmd, i, j in zip(*[iter(sys.stdin.read().split())]*3):
        i = int(i) - 1
        if cmd == "1":
            if S[i] == j:
                continue

            S[i] = j
            i0 = N + i
            T[i0] = smap[ord(j)-c0]
            while i0 > 1:
                i0 = i0 >> 1
                T[i0] = T[i0+i0] | T[i0-~i0]
        elif cmd == "2":
            i += N
            j = int(j) + N

            d1 = 0
            while i < j:
                if i & 1:
                    d1 |= T[i]
                    i += 1
                if j & 1:
                    j -= 1
                    d1 |= T[j]

                i >>= 1; j >>=1

            ans.append(popcnt2(d1))

    print(*ans, sep="\n")

def __starting_point():
    resolve()

__starting_point()

class SegTree:

    def __init__(self, N, S):
        self.N = 1
        while self.N < N:
            self.N *= 2
        self.tree = [0 for i in range(2 * self.N - 1)]
        for i in range(len(S)):
            self.tree[self.N - 1 + i] = 1 << (ord(S[i]) - ord('a'))
        for i in range(self.N - 2, -1, -1):
            self.tree[i] = self.tree[i * 2 + 1] | self.tree[i * 2 + 2]

    def update(self, index, char):
        i = self.N - 1 + index
        self.tree[i] = 1 << (ord(char) - ord('a'))

        while i > 0:
            i = (i - 1) // 2
            self.tree[i] = self.tree[i * 2 + 1] | self.tree[i * 2 + 2]

    def get(self, a, b):
        return self.query(a, b, 0, 0, self.N)

    def query(self, a, b, k, l, r):
        if r <= a or b <= l:
            return 0
        elif a <= l and r <= b:
            return self.tree[k]
        else:
            left = self.query(a, b, 2 * k + 1, l, (l + r) // 2)
            right = self.query(a, b, 2 * k + 2, (l + r) // 2, r)
            return left | right


def solve():

    N = int(input())
    S = list(input())
    tree = SegTree(N, S)

    Q = int(input())
    for _ in range(Q):
        t_q, in_1, in_2 = input().split()
        if t_q == '1':
            i_q = int(in_1)
            c_q = in_2
            tree.update(i_q-1, c_q)
        elif t_q == '2':
            l_q = int(in_1)
            r_q = int(in_2)
            print((bin(tree.get(l_q-1, r_q)).count('1')))


solve()


class SegmentTree(object):
    def __init__(self, sequence, function, identity):
        N = len(sequence)
        self.length = (1 << (N - 1)).bit_length()
        self.function = function
        self.data = [identity] * (self.length << 1)
        self.identity = identity
        # set values
        for i in range(N):
            self.data[i + self.length - 1] = sequence[i]
        # build
        for i in range(self.length - 2, -1, -1) :
            self.data[i] = self.function(self.data[(i << 1) + 1], self.data[(i << 1) + 2])

    def update(self, idx, x):
        idx += self.length - 1
        self.data[idx] = x
        while idx:
            idx = (idx - 1) >> 1
            self.data[idx] = self.function(self.data[(idx << 1) + 1], self.data[(idx << 1) + 2])

    def query(self, p, q):
        if q <= p:
            return self.identity
        p += self.length - 1
        q += self.length - 2
        res = self.identity
        while q - p > 1:
            if not p & 1:
                res = self.function(res, self.data[p])
            if q & 1:
                res = self.function(res, self.data[q])
                q -= 1
            p >>= 1
            q = (q - 1) >> 1
        return self.function(res, self.data[p]) if p == q else self.function(self.function(res, self.data[p]), self.data[q])

def popcount(n):
  n = (n & 0x5555555555555555) + ((n & 0xAAAAAAAAAAAAAAAA) >> 1)
  n = (n & 0x3333333333333333) + ((n & 0xCCCCCCCCCCCCCCCC) >> 2)
  n = (n & 0x0F0F0F0F0F0F0F0F) + ((n & 0xF0F0F0F0F0F0F0F0) >> 4)
  n = (n & 0x00FF00FF00FF00FF) + ((n & 0xFF00FF00FF00FF00) >> 8)
  n = (n & 0x0000FFFF0000FFFF) + ((n & 0xFFFF0000FFFF0000) >> 16)
  n = (n & 0x00000000FFFFFFFF) + ((n & 0xFFFFFFFF00000000) >> 32)
  return n

N = int(input())
S = input()
atoi = {chr(ord("a") + d): 1 << d for d in range(26)}
data = [atoi[c] for c in (S)]
seg_tree = SegmentTree(data, lambda a, b: a | b, 0)
Q = int(input())
for _ in range(Q):
    q, a, b = input().split()
    if q == "1":
        idx = int(a) - 1
        seg_tree.update(idx, atoi[b])
    else:
        print(popcount(seg_tree.query(int(a) - 1, int(b))))

class SegmentTree():
    '''
    非再帰
    segment tree
    '''

    def __init__(self, n, func, init=float('inf')):
        '''
        n->配列の長さ
        func:func(a,b)->val,　func=minだとRMQになる
        木の高さhとすると,
        n:h-1までのノード数。h段目のノードにアクセスするために使う。
        data:ノード。
        parent:k->child k*2+1とk*2+2
        '''
        self.n = 2**(n-1).bit_length()
        self.init = init
        self.data = [init]*(2*self.n)
        self.func = func

    def set(self, k, v):
        '''
        値の初期化
        '''
        self.data[k+self.n-1] = v

    def build(self):
        '''
        setの後に一斉更新
        '''
        for k in reversed(list(range(self.n-1))):
            self.data[k] = self.func(self.data[k*2+1], self.data[k*2+2])

    def update(self, k, a):
        '''
        list[k]=aに更新する。
        更新ぶんをrootまで更新
        '''
        k += self.n-1
        self.data[k] = a

        while k > 0:
            k = (k-1)//2
            self.data[k] = self.func(self.data[k*2+1], self.data[k*2+2])

    def query(self, l, r):
        '''
        [l,r)のfuncを求める
        '''
        L = l+self.n
        R = r+self.n
        ret = self.init
        while L < R:
            if R & 1:
                R -= 1
                ret = self.func(ret, self.data[R-1])
            if L & 1:
                ret = self.func(ret, self.data[L-1])
                L += 1
            L >>= 1
            R >>= 1
        return ret

N = int(input())
S = input()
Q = int(input())
Seg = SegmentTree(N, lambda x, y: x | y, 0)

def a2n(a):
    return ord(a) - ord('a')

def createBit(a):
    return 1 << a2n(a)

for i, s in enumerate(S):
    Seg.set(i, createBit(s))
Seg.build()

for _ in range(Q):
    t, l, r = input().split()
    if int(t) == 1:
        Seg.update(int(l)-1, createBit(r))
    else:
        print((bin(Seg.query(int(l)-1, int(r))).count('1')))

# N = int(input())
# S = input()
# Q = int(input())
# queries = [list(input().split()) for _ in range(Q)]


# def a2n(a):
#     return ord(a)-ord("a")


# def createInp(a):
#     return 1 << a2n(a)


# Seg = SegmentTree(len(S), lambda x, y: x | y, init=0)
# for i, s in enumerate(S):
#     Seg.set(i, createInp(s))
# Seg.build()

# for q, l, r in queries:
#     if q == "1":
#         Seg.update(int(l)-1, createInp(r))
#     else:
#         print(bin(Seg.query(int(l)-1, int(r))).count('1'))


# Segment tree (Bitwise Or)
from operator import or_


class SegmentTree:
    _f = None
    _data = None
    _offset = None
    _size = None

    def __init__(self, size, f):
        self._f = f
        self._size = size
        t = 1
        while t < size:
            t *= 2
        self._offset = t - 1
        self._data = [0] * (t * 2 - 1)

    def build(self, iterable):
        f = self._f
        data = self._data
        data[self._offset:self._offset + self._size] = iterable
        for i in range(self._offset - 1, -1, -1):
            data[i] = f(data[i * 2 + 1], data[i * 2 + 2])

    def update(self, index, value):
        f = self._f
        data = self._data
        i = self._offset + index
        data[i] = value
        while i >= 1:
            i = (i - 1) // 2
            data[i] = f(data[i * 2 + 1], data[i * 2 + 2])

    def query(self, start, stop):
        def iter_segments(data, l, r):
            while l < r:
                if l & 1 == 0:
                    yield data[l]
                if r & 1 == 0:
                    yield data[r - 1]
                l = l // 2
                r = (r - 1) // 2
        f = self._f
        it = iter_segments(self._data, start + self._offset,
                           stop + self._offset)
        result = next(it)
        for e in it:
            result = f(result, e)
        return result


def conv(c):
    return 1 << (ord(c) - ord('a'))


N = int(input())
S = input()

st = SegmentTree(N, or_)
st.build(conv(c) for c in S)

Q = int(input())
for _ in range(Q):
    q = input().split()
    if q[0] == '1':
        i, c = q[1:]
        i = int(i) - 1
        st.update(i, conv(c))
    elif q[0] == '2':
        l, r = list(map(int, q[1:]))
        print((bin(st.query(l - 1, r)).count('1')))


import bisect

N = int(input())
S = list(str(input()))

def ordN(x):
  return ord(x) - ord('a')

li = [[] for _ in range(26)]
for i,s in enumerate(S):
  li[ordN(s)].append(i)

for i in range(int(input())):
  Q1, Q2, Q3 = input().split()
  if Q1 == "1":
    Q2 = int(Q2) - 1
    if S[Q2] != Q3:
      I = bisect.bisect_left(li[ordN(S[Q2])], Q2)
      li[ordN(S[Q2])].pop(I)
      bisect.insort(li[ordN(Q3)], Q2)
      S[Q2] = Q3
  else:
    Q2, Q3 = int(Q2)-1, int(Q3)-1
    count = 0
    for j in range(26):
      I = bisect.bisect_left(li[j], Q2)
      if I < len(li[j]) and li[j][I] <= Q3:
        count += 1
    print(count)

import sys
readline = sys.stdin.readline

from bisect import bisect_left, insort_left

def main():
    n = int(readline())
    s = list(readline().rstrip())
    q = int(readline())
    
    chk_abc = [[] for i in range(26)]
    for i, si in enumerate(s):
        ci = ord(si) - 97
        chk_abc[ci].append(i)
        
    ans = []
    for _ in range(q):
        cmd, i, j = readline().rstrip().split()
        i = int(i) - 1
        
        if cmd == "1":
            if s[i] == j:
                continue
            c1 = ord(s[i]) - 97
            prev = chk_abc[c1]
            c2 = ord(j) - 97
            prev.pop(bisect_left(prev, i))
            s[i] = j
            insort_left(chk_abc[c2], i)
            
        else:
            j = int(j) - 1
            num = 0
            for chk in chk_abc:
                le = len(chk)
                k = bisect_left(chk, i, 0, le)
                if k == le:
                    continue
                if i <= chk[k] <= j:
                    num += 1
            ans.append(num)

    print(*ans, sep="\n")

def __starting_point():
    main()
__starting_point()

f=input
from bisect import bisect_left as g, insort as h
n,s=int(f()),list(f())
d={chr(97+i):[] for i in range(26)}
for i in range(n): d[s[i]]+=[i]
for _ in range(int(f())):
  a,b,c=f().split(); b=int(b)-1
  if a>'1': print(sum(1 for l in d.values() if g(l,b)<len(l) and l[g(l,b)]<int(c)))
  elif s[b]!=c: l=d[s[b]]; l.pop(g(l,b)); s[b]=c; h(d[c],b)

import sys
from bisect import bisect_left, bisect_right, insort

sr = lambda: sys.stdin.readline().rstrip()
ir = lambda: int(sr())
lr = lambda: list(map(int, sr().split()))

N = ir()
S = list('-' + sr())
d = [[] for _ in range(26)]
for i in range(1, N+1):
    s = S[i]
    o = ord(s) - ord('a')
    d[o].append(i)

Q = ir()
for _ in range(Q):
    q, a, b = sr().split()
    if q == '1':
        a = int(a)
        if S[a] == b:
            continue
        prev = ord(S[a]) - ord('a')
        d[prev].pop(bisect_left(d[prev], a))
        next = ord(b) - ord('a')
        insort(d[next], a)
        S[a] = b
    else:
        a = int(a); b = int(b)
        ans = 0
        for alpha in range(26):
            if bisect_right(d[alpha], b) - bisect_left(d[alpha], a) >= 1:
                ans += 1
        print(ans)


from operator import or_
 
class SegmentTree:
    def __init__(self, size, op, e):
        self._op = op
        self._e = e
        self._size = size
        t = 1 << (size - 1).bit_length()
        self._offset = t - 1
        self._data = [e] * (t * 2 - 1)
 
    def __getitem__(self, key):
        return self._data[self._offset + key]
 
    def __setitem__(self, key, value):
        op = self._op
        data = self._data
        i = self._offset + key
        data[i] = value
        while i >= 1:
            i = (i - 1) // 2
            data[i] = op(data[i * 2 + 1], data[i * 2 + 2])
 
    def build(self, iterable):
        op = self._op
        data = self._data
        data[self._offset:self._offset + self._size] = iterable
        for i in range(self._offset - 1, -1, -1):
            data[i] = op(data[i * 2 + 1], data[i * 2 + 2])
 
    def query(self, start, end):
        op = self._op
        res = self._e
        l = self._offset + start
        r = self._offset + end
        while l < r:
            if l & 1 == 0:
                res = op(res, self._data[l])
            if r & 1 == 0:
                res = op(res, self._data[r - 1])
            l = l // 2
            r = (r-1) // 2
        return res
 
def str2bit(c):
  return 1 << (ord(c) - ord('a'))
 
N = int(input())
S = input()
 
st = SegmentTree(N, or_, 0)
st.build(str2bit(x) for x in S)
 
M = int(input())
for i in range(M):
  q, a, b = input().split()
  if q == "1":
    st[int(a)-1] = str2bit(b)
  elif q == "2":
    print(bin(st.query(int(a)-1,int(b))).count("1"))

from bisect import insort_left, bisect_left
n = int(input())
s = list(input())
Q = int(input())
query = [input() for _ in range(Q)]
dic = {chr(i+ord('a')):[] for i in range(26)}
for ind, c in enumerate(s):
    dic[c].append(ind)

for q in query:
    t, a, b = q.split()
    a = int(a)-1
    if t=='1':
        if s[a]==b:
            continue
        ind = bisect_left(dic[s[a]], a)
        dic[s[a]].pop(ind)
        insort_left(dic[b], a)
        s[a] = b
    else:
        b = int(b)-1
        tmp = 0
        for li in list(dic.values()):
            if li and a<=li[-1] and li[bisect_left(li, a)] <= b:
                tmp += 1
        print(tmp)


from collections import defaultdict
from bisect import bisect, insort, bisect_left
n = int(input())
s = list(input())
q = int(input())
d = defaultdict(list)
for i in range(n):
    d[s[i]] += [i]
for _ in range(q):
    t, i, c = input().split()
    t, i = int(t), int(i) - 1
    if t == 1:
        if s[i] == c:
            continue
        d[s[i]].pop(bisect(d[s[i]], i) - 1)
        s[i] = c
        insort(d[c], i)
    else:
        res = 0
        c = int(c) - 1
        for j in d.values():
            if bisect(j, c) - bisect_left(j, i):
                res += 1
        print(res)

from bisect import bisect_left
import string 

dic={c:[] for c in string.ascii_lowercase}
N=int(input())
S=list(input())
Q=int(input())
for i in range(len(S)):
  dic[S[i]].append(i+1)

for i in range(Q):
  a,b,c=list(map(str,input().split()))
  if a=='1':
    if S[int(b)-1]==c:
      continue
    b=int(b)
    f=S[b-1]
    d=bisect_left(dic[f],b)
    e=bisect_left(dic[c],b)
    dic[f].pop(d)
    dic[c].insert(e,b)
    S[b-1]=c
  else:
    ans=0
    b,c=int(b),int(c)
    for j in string.ascii_lowercase:
      d=bisect_left(dic[j],b)
      if d<len(dic[j]):
        if dic[j][d]<=c:
          ans+=1
    print(ans)


import bisect
N=input()
S=list(input())
alphabet=[[] for _ in range(26)]
for i,s in enumerate(S):
    alphabet[ord(s)-ord('a')].append(i)
Q=int(input())
for _ in range(Q):
    Type,A,B=input().split()
    if Type=="1":
        A=int(A)
        s=S[A-1]
        if s==B:
            continue
        o=ord(s)-ord("a")
        alphabet[o].pop(bisect.bisect_left(alphabet[o],A-1))
        index=bisect.bisect(alphabet[ord(B)-ord("a")],A-1)
        alphabet[ord(B)-ord("a")].insert(index,A-1)
        S[A-1]=B
    else:
        A=int(A)
        B=int(B)
        ans=0
        for i in range(26):
            indexL=bisect.bisect_left(alphabet[i],A-1)
            #indexR=bisect.bisect_right(alphabet[i],B-1)
            #ans+=(indexL!=indexR)
            if indexL==len(alphabet[i]):
                continue
            ans+=(alphabet[i][indexL]<=B-1)
        print(ans)


"""
AtCoder Beginner Contest 157  E - Simple String Queries

文字列Sに対して、
クエリ1：i番目の文字をcに変更
クエリ2：S[l~r]に何種類の文字があるか

各クエリについて、律儀に文字を変えて、l~rについて何種類あるか調べると間に合わないはず。

~~~ABCCDC~~~
        ↑を別の文字にした時に、最後に出てくるCがその二個左、のような情報をちゃんと残したい。
→ 最後の位置が知りたいとかではなく、lとrを指定した時に、その間にあるかが知りたい（具体的な位置が知りたいわけではない）
　そうするt、a〜zについて、l~rに存在するか、みたいな確認ができそう

N <= 5 * 10**5
Q <= 2 * 10**4
なので、bisect使えば間に合うのか？よくわからん

Sとしてではなく、現時点で何文字目がなになのかも持っておいたほうが楽か。

"""

import bisect

N = int(input())
S = input()
Q = int(input())

# 現時点でどこに何があるか
cS = [""] * N
# ある文字の位置
char_idxs = [[]for _  in range(26)]

# i番目は文字s、を保存
for i,s in enumerate(S):
    cS[i] = s
    char_idxs[ord(s) - 97].append(i)

query = [tuple(map(str, input().split())) for _ in range(Q)]




for q,a,b in query:
    
    # q == 1の時、a番目の文字をbに置き換え
    if q == "1":
        # 現時点の文字と置き換え後が同じなら何もしない
        if cS[int(a) - 1] == b:
            continue
        else:
            #print("---", char_idxs[ord(cS[]) - 97], int(a)-1, bisect.bisect_right(char_idxs[ord(b) - 97], int(a)-1))
            # a番目の文字を書き換える

            
            idx = bisect.bisect_right(char_idxs[ord(cS[int(a)-1]) - 97], int(a)-1)
            # idxの左の値がa番目であれば、それを消す
            if char_idxs[ord(cS[int(a)-1]) - 97][idx-1] == int(a)-1:
                # delの方が早いか？
                char_idxs[ord(cS[int(a)-1]) - 97][idx-1:idx] = []
            
            # bの文字がa番目にあるよ、を保存
            bisect.insort_right(char_idxs[ord(b) - 97], int(a) - 1)

            # 置き換え
            cS[int(a) - 1] = b

    # q == 2の時、範囲の文字種の個数を調べる
    else:
        cnt = 0
        for i in range(26):
            # そもそもその文字が文字列全体になければ考えない
            if len(char_idxs[i]) == 0: continue
            # bisect_leftで a-1 を入れられる場所を探して、その時に右にある値がb-1以下であれば含まれる
            l = bisect.bisect_left(char_idxs[i], int(a)-1)
            #r = bisect.bisect_right(char_idxs[i], int(b)-1)

            # char_idxs[i][l]はa-1より右に文字種iが初めて出てくる位置なので、それがb-1を含む右側にあればOK
            if l < len(char_idxs[i]) and char_idxs[i][l] <= int(b)-1:
                cnt += 1

        print(cnt)




from bisect import bisect_left,bisect_right
import string 

dic = {c:[] for c in string.ascii_lowercase}

n = int(input())
s = list(input())
q = int(input())

for i,c in enumerate(s,start=1):
  dic[c].append(i)
  
for i in range(q):
  p,l,r = list(map(str,input().split()))
  if p =='1':
    j = int(l)
    if r == s[j-1]:
      continue
    c = s[j-1]    
    dic[c].pop(bisect_left(dic[c],j))
    dic[r].insert(bisect_left(dic[r],j),j)
    s[j-1] = r
  else:
    ans = 0
    l = int(l)
    r = int(r)
    for c in string.ascii_lowercase:
      pl = bisect_left(dic[c],l)
      if pl < len(dic[c]):
        if dic[c][pl] <= r:
          ans += 1
    print(ans)



import bisect

N = int(input())
S = input()
Q = int(input())

# 現時点でどこに何があるか
cS = [""] * N
# ある文字の位置
char_idxs = [[]for _  in range(26)]

# i番目は文字s、を保存
for i,s in enumerate(S):
    cS[i] = s
    char_idxs[ord(s) - 97].append(i)

query = [tuple(map(str, input().split())) for _ in range(Q)]




for q,a,b in query:
    
    # q == 1の時、a番目の文字をbに置き換え
    if q == "1":
        # 現時点の文字と置き換え後が同じなら何もしない
        if cS[int(a) - 1] == b:
            continue
        else:
            #print("---", char_idxs[ord(cS[]) - 97], int(a)-1, bisect.bisect_right(char_idxs[ord(b) - 97], int(a)-1))
            # a番目の文字を書き換える

            
            idx = bisect.bisect_right(char_idxs[ord(cS[int(a)-1]) - 97], int(a)-1)
            # idxの左の値がa番目であれば、それを消す
            if char_idxs[ord(cS[int(a)-1]) - 97][idx-1] == int(a)-1:
                # delの方が早いか？
                char_idxs[ord(cS[int(a)-1]) - 97][idx-1:idx] = []
            
            # bの文字がa番目にあるよ、を保存
            bisect.insort_right(char_idxs[ord(b) - 97], int(a) - 1)

            # 置き換え
            cS[int(a) - 1] = b

    # q == 2の時、範囲の文字種の個数を調べる
    else:
        cnt = 0
        for i in range(26):
            # そもそもその文字が文字列全体になければ考えない
            if len(char_idxs[i]) == 0: continue
            # bisect_leftで a-1 を入れられる場所を探して、その時に右にある値がb-1以下であれば含まれる
            l = bisect.bisect_left(char_idxs[i], int(a)-1)
            #r = bisect.bisect_right(char_idxs[i], int(b)-1)

            # char_idxs[i][l]はa-1より右に文字種iが初めて出てくる位置なので、それがb-1を含む右側にあればOK
            if l < len(char_idxs[i]) and char_idxs[i][l] <= int(b)-1:
                cnt += 1

        print(cnt)




from bisect import bisect_left,bisect_right

TO_ACII = 97

N = int(input())
S = ["0"] + list(input())
Q = int(input())

ABC = [[]for i in range(0,26,1)]#該当文字a:0 b:1 ...z:26 が何文字目に出てきたかを保持する配列

for i in range(1,N+1,1):
    ABC[ord(S[i])-TO_ACII].append(i)

ans = []
for i in range(0,Q,1):
    q = list(input().split())
    if q[0]=="1":#文字変更
        changed = S[int(q[1])]
        ABC[ord(changed)-TO_ACII].pop(bisect_left(ABC[ord(changed)-TO_ACII],int(q[1])))
        ABC[ord(q[2])-TO_ACII].insert(bisect_left(ABC[ord(q[2])-TO_ACII],int(q[1])),int(q[1]))
        S[int(q[1])]=q[2]
    else:
        tmpans = 0
        for i in range(0,26,1):

            if bisect_right(ABC[i],int(q[1])-1)<len(ABC[i]) and int(q[2]) >= ABC[i][bisect_right(ABC[i],int(q[1])-1)]:
                tmpans+=1
        ans.append(tmpans)

for i in range(0,len(ans),1):
    print((ans[i]))


import sys
# import re
import math
import collections
# import decimal
import bisect
import itertools
import fractions
# import functools
import copy
# import heapq
import decimal
# import statistics
import queue

# import numpy as np

sys.setrecursionlimit(10000001)
INF = 10 ** 16
MOD = 10 ** 9 + 7
# MOD = 998244353

ni = lambda: int(sys.stdin.readline())
ns = lambda: list(map(int, sys.stdin.readline().split()))
na = lambda: list(map(int, sys.stdin.readline().split()))


# ===CODE===
def main():
    n = ni()
    s = list(input())
    q = ni()

    alphabet_idx = {}
    for i in range(26):
        alphabet_idx[i] = []

    for i, si in enumerate(s):
        alphabet_idx[ord(si) - ord("a")].append(i)

    for _ in range(q):
        qi, a, b = input().split()
        qi, a = int(qi), int(a)

        if qi == 1:
            a -= 1
            if s[a] == b:
                continue
            del_char = ord(s[a]) - ord("a")
            del_idx = bisect.bisect_left(alphabet_idx[del_char], a)
            del alphabet_idx[del_char][del_idx]

            insert_char = ord(b) - ord("a")
            insert_idx = bisect.bisect_left(alphabet_idx[insert_char], a)
            alphabet_idx[insert_char].insert(insert_idx, a)

            s[a] = b

        if qi == 2:
            b = int(b)
            a -= 1
            b -= 1
            ans = 0

            for i in range(26):
                a_idx = bisect.bisect_left(alphabet_idx[i], a)
                if a_idx < len(alphabet_idx[i]):
                    if alphabet_idx[i][a_idx] <= b:
                        ans += 1

            print(ans)


def __starting_point():
    main()

__starting_point()

n=int(input())+1
data=[0]*n*2
def update(i,x):
    i+=n
    data[i]=x
    i//=2
    while i:
        data[i]=data[i*2]|data[i*2+1]
        i//=2
def query(l,r):
    l+=n
    r+=n
    s=0
    while l<r:
        if l&1:
            s|=data[l]
            l+=1
        if r&1:
            r-=1
            s|=data[r]
        l//=2
        r//=2
    return sum(map(int,bin(s)[2:]))
s=input()
for i,c in enumerate(s,n):
    data[i]=1<<(ord(c)-97)
for i in range(n-1,0,-1):
    data[i]=data[2*i]|data[2*i+1]
for _ in range(int(input())):
    q,a,b=input().split()
    if q=="2":
        print(query(int(a)-1,int(b)))
    else:
        update(int(a)-1,1<<(ord(b)-97))

class SegmentTree:
    def __init__(self, n, segment_func, identity_element):
        self.n = n
        self.num = 1 << (self.n - 1).bit_length()
        self.segment_func = segment_func
        self.identity_element = identity_element
        self.segment_tree = [self.identity_element] * (2 * self.num)

    def initialize(self, val_arr):
        for i in range(self.n):
            self.segment_tree[self.num + i] = val_arr[i]
        for i in range(self.num - 1, 0, -1):
            self.segment_tree[i] = self.segment_func(self.segment_tree[2 * i], self.segment_tree[2 * i + 1])

    def update(self, idx, val):
        idx += self.num
        self.segment_tree[idx] = val
        while idx > 1:
            self.segment_tree[idx >> 1] = self.segment_func(self.segment_tree[idx], self.segment_tree[idx ^ 1])
            idx >>= 1

    def query(self, left, right):
        res = self.identity_element
        left += self.num
        right += self.num
        while left < right:
            if left & 1:
                res = self.segment_func(res, self.segment_tree[left])
                left += 1
            if right & 1:
                res = self.segment_func(res, self.segment_tree[right - 1])
            left >>= 1
            right >>= 1
        return res

    def bisect_left(self, left, right, target):
        left += self.num
        right += self.num
        left_min = -1
        right_min = -1
        while left < right:
            if left & 1:
                if self.segment_tree[left] >= target and left_min == -1:
                    left_min = left
                left += 1
            if right & 1:
                if self.segment_tree[right - 1] >= target:
                    right_min = right - 1
            left >>= 1
            right >>= 1
        if left_min != -1 or right_min != -1:
            res = left_min if left_min != -1 else right_min
            while res < self.num:
                if self.segment_tree[2 * res] >= target:
                    res *= 2
                else:
                    res *= 2
                    res += 1
            return res - self.num
        else:
            return self.n

    def get_val(self, idx):
        return self.segment_tree[self.num + idx]


def conv_to_int(x):
    return 1 << (ord(x) - ord('a'))


def main():
    n = int(input())
    s = [conv_to_int(t) for t in list(input())]
    q = int(input())
    st = SegmentTree(n, lambda x, y: x | y, 0)
    st.initialize(s)
    for _ in range(q):
        query = input().split()
        if query[0] == '1':
            st.update(int(query[1]) - 1, conv_to_int(query[2]))
        else:
            res = st.query(int(query[1]) - 1, int(query[2]))
            ans = 0
            while res:
                ans += res & 1
                res >>= 1
            print(ans)


def __starting_point():
    main()

__starting_point()

import sys
# import re
import math
import collections
# import decimal
import bisect
import itertools
import fractions
# import functools
import copy
# import heapq
import decimal
# import statistics
import queue

# import numpy as np

sys.setrecursionlimit(10000001)
INF = 10 ** 16
MOD = 10 ** 9 + 7
# MOD = 998244353

ni = lambda: int(sys.stdin.readline())
ns = lambda: list(map(int, sys.stdin.readline().split()))
na = lambda: list(map(int, sys.stdin.readline().split()))


# ===CODE===
def main():
    n = ni()
    s = list(input())
    q = ni()

    alphabet_idx = {}
    for i in range(26):
        alphabet_idx[i] = []

    for i, si in enumerate(s):
        alphabet_idx[ord(si) - ord("a")].append(i)

    for _ in range(q):
        qi, a, b = input().split()
        qi, a = int(qi), int(a)

        if qi == 1:
            a -= 1
            if s[a] == b:
                continue
            current_char = ord(s[a]) - ord("a")
            del_idx = bisect.bisect_left(alphabet_idx[current_char], a)
            del alphabet_idx[current_char][del_idx]

            insert_idx = bisect.bisect_left(alphabet_idx[ord(b) - ord("a")], a)
            alphabet_idx[ord(b) - ord("a")].insert(insert_idx, a)

            s[a] = b

        if qi == 2:
            b = int(b)
            a -= 1
            b -= 1
            ans = 0

            for i in range(26):
                a_idx = bisect.bisect_left(alphabet_idx[i], a)
                if a_idx < len(alphabet_idx[i]):
                    if alphabet_idx[i][a_idx] <= b:
                        ans += 1

            print(ans)


def __starting_point():
    main()

__starting_point()

class SegmentTree:
  """Segmant Tree for query process.

  Args:
    a (List[int]): array.
    identity (int): identity element with f.
  """
  def __init__(self, a, identity=0):
    self.a = a
    self.n = len(a)
    self.identity = identity
    self.depth = (self.n - 1).bit_length()
    self.segment = (1 << self.depth) - 1
    self.tree = [identity] * (self.segment * 2 + 1)
    self.tree[self.segment : self.n + self.segment] = a
    for i in range(self.segment - 1, -1, -1):
      self.tree[i] = self.f(self.tree[2*i+1], self.tree[2*i+2])

  def update(self, i: int, x: int):
    i += self.segment
    self.tree[i] = x
    while i:
      i = (i - 1) // 2
      self.tree[i] = self.f(self.tree[2*i+1], self.tree[2*i+2])

  def query(self, l: int, r: int) -> int:
    """Query at [l, r]

    Args:
      l (int): left
      r (int): right

    Returns:
      int: result
    """
    if l > r:
      return self.identity
    l += self.segment
    r += self.segment
    res = self.identity
    while l <= r:
      if not l & 1:
        res = self.f(self.tree[l], res)
      if r & 1:
        res = self.f(res, self.tree[r])
      l = l // 2
      r = (r - 2) // 2
    return res

  def f(self, x: int, y: int) -> int:
    return x | y

n = int(input())
s = input()
q = int(input())
a = ord('a')
segment_tree = SegmentTree([1 << (ord(c) - a) for c in s])
for _ in range(q):
  query = input().split()
  if query[0] == '1':
    i = int(query[1]) - 1
    c = query[2]
    segment_tree.update(i, 1 << (ord(c) - a))
  else:
    l = int(query[1]) - 1
    r = int(query[2]) - 1
    bit = segment_tree.query(l, r)
    print(format(bit, 'b').count('1'))

class SegmentTree():
    """A segment Tree.

    This is a segment tree without recursions.
    This can support queries as follows:
     - update a single value in O(logN).
     - get the folded value of values in a segment [l, r) in O(logN)
    N is the length of the given iterable value.

    Parameters
    ----------
    iterable : Iterable[_T]
        An iterable value which will be converted into a segment tree
    func : Callable[[_T, _T], _T]
        A binary function which returns the same type as given two.
        This has to satisfy the associative law:
            func(a, func(b, c)) = func(func(a, b), c)
    e : _T
        The identity element of the given func.
        In other words, this satisfies:
            func(x, e) = func(e, x) = x
    """
    
    def __init__(self, iterable, func, e):
        self.func = func
        self.e = e

        ls = list(iterable)
        self.n = 1 << len(ls).bit_length()
        ls.extend( [self.e] * (self.n - len(ls)) )

        self.data = [self.e] * self.n + ls
        for i in range(self.n-1, 0, -1):
            self.data[i] = self.func(self.data[2*i], self.data[2*i+1])

    def replace(self, index, value):
        """replace the old value of the given index with the given new value.

        This replaces the old value of the given index with the given new value in O(logN).
        This is like "list[index] = value".

        Parameters
        ----------
        index : int
            The index of the value which will be replaced.
        value : _T
            The new value with which the old value will be replaced.
        """
        index += self.n
        self.data[index] = value
        index //= 2
        while index > 0:
            self.data[index] = self.func(self.data[2*index], self.data[2*index+1])
            index //= 2
        
    def folded(self, l, r):
        """get the folded value of values in a segment [l, r).

        This get the folded value of values in a segment [l, r) in O(logN).
        If func is add, it returns the sum of values in [l, r).
        In other words, this is eqivalent to "sum(list[l:r])".
        If func is other functions, then this is equivalent to "accumulate(list[l:r], func)".

        Parameters
        ----------
        l : int
            The left edge.
        r : int
            The right edge.

        Returns
        -------
        _T(the same type as the type of the element of the given iterable)
            This is equivalent to func(list[l], func(list[l+1], ... ) ).
            If func is represented as '*', then it's:
            list[l] * list[l+1] * ... * list[r-1] 
        """

        left_folded = self.e
        right_folded = self.e
        l += self.n
        r += self.n
        while l < r:
            if l % 2:
                left_folded = self.func(left_folded, self.data[l])
                l += 1
            if r % 2:
                r -= 1
                right_folded = self.func(self.data[r], right_folded)
            l //= 2
            r //= 2
        
        return self.func(left_folded, right_folded)


from operator import or_

N = int(input())
S = input()

ls = [1 << (ord(c) - ord('a')) for c in S]
segtree = SegmentTree(ls, or_, 0)

Q = int(input())
for _ in range(Q):
    s = input()
    if s[0] == '1':
        i, c = s[2:].split()
        segtree.replace(int(i)-1, 1 << (ord(c) - ord('a')))
    else:
        l, r = list(map(int, s[2:].split()))
        value = segtree.folded(l-1, r)
        print((format(value, 'b').count('1')))


from operator import or_

class SegmentTree:
    def __init__(self, size, op, e):
        self._op = op
        self._e = e
        self._size = size
        t = 1 << (size - 1).bit_length()
        self._offset = t - 1
        self._data = [e] * (t * 2 - 1)
 
    def __getitem__(self, key):
        return self._data[self._offset + key]
 
    def __setitem__(self, key, value):
        op = self._op
        data = self._data
        i = self._offset + key
        data[i] = value
        while i >= 1:
            i = (i - 1) // 2
            data[i] = op(data[i * 2 + 1], data[i * 2 + 2])
 
    def build(self, iterable):
        op = self._op
        data = self._data
        data[self._offset:self._offset + self._size] = iterable
        for i in range(self._offset - 1, -1, -1):
            data[i] = op(data[i * 2 + 1], data[i * 2 + 2])
 
    def query(self, start, end):
        op = self._op
        res = self._e
        l = self._offset + start
        r = self._offset + end
        while l < r:
            if l & 1 == 0:
                res = op(res, self._data[l])
            if r & 1 == 0:
                res = op(res, self._data[r - 1])
            l = l // 2
            r = (r-1) // 2
        return res

def str2bit(c):
  return 1 << (ord(c) - ord('a'))

N = int(input())
S = input()

st = SegmentTree(N, or_, 0)
st.build(str2bit(x) for x in S)

M = int(input())
for i in range(M):
  q, a, b = input().split()
  if q == "1":
    st[int(a)-1] = str2bit(b)
  elif q == "2":
    print(bin(st.query(int(a)-1,int(b))).count("1"))

class SegmentTree:
    """
    0-indexed
    query : [L, R)
    """
    def __init__(self, size, initValue):
        self.size = 1 << (size.bit_length())  # 完全二分木にする
        self.data = [initValue] * (2 * self.size - 1)
        self.initValue = initValue

    def cmpFunc(self, left, right):
        return left | right

    def build(self, rawData):
        self.data[self.size - 1: self.size - 1 + len(rawData)] = rawData
        for i in range(self.size - 1)[:: -1]:
            self.data[i] = self.cmpFunc(self.data[2 * i + 1], self.data[2 * i + 2])

    def update(self, index, value):
        index += self.size - 1
        self.data[index] = value
        while index >= 0:
            index = (index - 1) // 2
            self.data[index] = self.cmpFunc(self.data[2 * index + 1], self.data[2 * index + 2])

    def query(self, left, right):
        L = left + self.size
        R = right + self.size
        ret = self.initValue
        while L < R:
            if R & 1:
                R -= 1
                ret = self.cmpFunc(ret, self.data[R - 1])
            if L & 1:
                ret = self.cmpFunc(ret, self.data[L - 1])
                L += 1
            L >>= 1
            R >>= 1
        return ret

def cToI(s):
    return ord(s) - ord('a')

N = int(input())
S = list(map(cToI, input()))

S = [1 << s for s in S]
tree = SegmentTree(N, 0)
tree.build(S)

Q = int(input())
ans = []
for _ in range(Q):
    q1, q2, q3 = input().split()

    if q1 == '1':
        tree.update(int(q2) - 1, 1 << cToI(q3))
    else:
        ans.append(tree.query(int(q2) - 1, int(q3)))

for a in ans:
    cnt = 0
    while a > 0:
        cnt += a % 2
        a //= 2
    print(cnt)


import bisect

N=int(input())
S=list(str(input()))

def ci(x):
  return "abcdefghijklmnopqrstuvwxyz".find(x)

d=[[] for _ in range(26)]
for i,s in enumerate(S):
  d[ci(s)].append(i)

for i in range(int(input())):
  t,x,y=input().split()
  if t=="1":
    x=int(x)-1
    if S[x]!=y:
      l=bisect.bisect_left(d[ci(S[x])],x)
      d[ci(S[x])].pop(l)
      bisect.insort(d[ci(y)],x)
      S[x]=y
  else:
    x,y=int(x)-1,int(y)-1
    c=0
    for j in range(26):
      l=bisect.bisect_left(d[j],x)
      if l<len(d[j]) and d[j][l] <= y:
        c += 1
    print(c)


import bisect
n = int(input())
s = list(input())
alp = 'abcdefghijklmnopqrstuvwxyz'
cnt = {c: [] for c in alp}

for i, c in enumerate(s):
    cnt[c].append(i)

q = int(input())
for _ in range(q):
    t, x, y = input().split()
    ans = 0
    if t == '1':
        i, c = int(x) - 1, y
        if s[i] == c:
            continue
        a = bisect.bisect_left(cnt[s[i]], i)
        cnt[s[i]].pop(a)
        s[i] = c
        b = bisect.bisect_left(cnt[c], i)
        cnt[c].insert(b, i)
    else:
        l, r = int(x) - 1, int(y) - 1
        for c in alp:
            a = bisect.bisect_left(cnt[c], l)
            if a < len(cnt[c]) and cnt[c][a] <= r:
                ans += 1

        print(ans)

from bisect import insort_left, bisect_left
N = int(input())
S = list(input())
Q = int(input())
Query = [input() for _ in range(Q)]
dic = {chr(i+ord("a")):[] for i in range(26)}
for i, s in enumerate(S):
    dic[s].append(i)
for q in Query:
    t,a,b = q.split()
    a = int(a)-1
    if(t == "1"):
        if(S[a]==b):
            continue
        tmp_index = bisect_left(dic[S[a]],a)
        dic[S[a]].pop(tmp_index)
        insort_left(dic[b],a)
        S[a] = b
    elif(t == "2"):
        b = int(b)-1
        cnt = 0
        for li in dic.values():
            if li and a<=li[-1] and li[bisect_left(li, a)] <= b:
                cnt += 1
        print(cnt)

#!python3

import sys

iim = lambda: map(int, input().rstrip().split())

def resolve():
    N = int(input())
    S = list(input())
    Q = int(input())

    c0 = ord('a')
    smap = [1<<(i-c0) for i in range(c0, ord('z')+1)]

    T = [0]*N + [smap[ord(S[i])-c0] for i in range(N)]

    for i in range(N-1, 0, -1):
        i2 = i << 1
        T[i] = T[i2] | T[i2|1]

    ans = []
    #print(T)
    for cmd, i, j in zip(*[iter(sys.stdin.read().split())]*3):
        i = int(i) - 1
        if cmd == "1":
            if S[i] == j:
                continue

            S[i] = j
            i0 = N + i
            T[i0] = smap[ord(j)-c0]
            while i0 > 1:
                i0 = i0 >> 1
                T[i0] = T[i0+i0] | T[i0-~i0]
        elif cmd == "2":
            i += N
            j = int(j) + N

            d1 = 0
            while i < j:
                if i & 1:
                    d1 |= T[i]
                    i += 1
                if j & 1:
                    j -= 1
                    d1 |= T[j]

                i >>= 1; j >>=1

            ans.append(bin(d1).count('1'))

    print(*ans, sep="\n")

def __starting_point():
    resolve()

__starting_point()

def solve():
    import sys
    input = sys.stdin.readline

    ordA = ord('a')

    N = int(input())
    Ss = input().strip()
    nos = [ord(S)-ordA for S in Ss]
    Q = int(input())

    idEle = 0
    def _binOpe(x, y):
        return x | y
    def makeSegTree(numEle):
        numPow2 = 2 ** (numEle-1).bit_length()
        data = [idEle] * (2*numPow2)
        return data, numPow2
    def setInit(As):
        for iST, A in enumerate(As, numPow2):
            data[iST] = A
        for iST in reversed(list(range(1, numPow2))):
            data[iST] = _binOpe(data[2*iST], data[2*iST+1])
    def _update1(iA, A):
        iST = iA + numPow2
        data[iST] = A
    def update(iA, A):
        _update1(iA, A)
        iST = iA + numPow2
        while iST > 1:
            iST >>= 1
            data[iST] = _binOpe(data[2*iST], data[2*iST+1])
    def getValue(iSt, iEn):
        L = iSt + numPow2
        R = iEn + numPow2
        ans = idEle
        while L < R:
            if L & 1:
                ans = _binOpe(ans, data[L])
                L += 1
            if R & 1:
                R -= 1
                ans = _binOpe(ans, data[R])
            L >>= 1
            R >>= 1
        return ans

    data, numPow2 = makeSegTree(N)

    As = [(1<<no) for no in nos]
    setInit(As)

    anss = []
    for _ in range(Q):
        tp, v1, v2 = input().split()
        if tp == '1':
            pos = int(v1)-1
            c = ord(v2)-ordA
            update(pos, 1<<c)
        else:
            L, R = int(v1)-1, int(v2)-1
            v = getValue(L, R+1)
            ans = bin(v).count('1')
            anss.append(ans)

    print(('\n'.join(map(str, anss))))


solve()


#!python3

import sys

iim = lambda: map(int, input().rstrip().split())

def popcnt2(n):
    a = (
        0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
    )
    ans = 0
    while n:
        ans += a[n&0xff]
        n >>= 8
    return ans

def resolve():
    N = int(input())
    S = list(input())
    Q = int(input())

    c0 = ord('a')
    smap = [1<<(i-c0) for i in range(c0, ord('z')+1)]

    T = [0]*N + [smap[ord(S[i])-c0] for i in range(N)]

    for i in range(N-1, 0, -1):
        i2 = i << 1
        T[i] = T[i2] | T[i2|1]

    ans = []
    #print(T)
    for cmd, i, j in (line.split() for line in sys.stdin.readlines()):
        i = int(i) - 1
        if cmd == "1":
            if S[i] == j:
                continue

            S[i] = j
            i0 = N + i
            T[i0] = smap[ord(j)-c0]
            while i0 > 1:
                i0 = i0 >> 1
                T[i0] = T[i0+i0] | T[i0-~i0]
        elif cmd == "2":
            i += N
            j = int(j) + N

            d1 = 0
            while i < j:
                if i & 1:
                    d1 |= T[i]
                    i += 1
                if j & 1:
                    j -= 1
                    d1 |= T[j]

                i >>= 1; j >>=1

            ans.append(popcnt2(d1))

    print(*ans, sep="\n")

def __starting_point():
    resolve()

__starting_point()

def segfunc(x, y):
    return (x | y)


ide_ele = 0


class SegTree:
    """
    init(init_val, ide_ele): 配列init_valで初期化 O(N)
    update(k, x): k番目の値をxに更新 O(logN)
    query(l, r): 区間[l, r)をsegfuncしたものを返す O(logN)
    """

    def __init__(self, init_val, segfunc, ide_ele):
        """
        init_val: 配列の初期値
        segfunc: 区間にしたい操作
        ide_ele: 単位元
        n: 要素数
        num: n以上の最小の2のべき乗
        tree: セグメント木(1-index)
        """
        n = len(init_val)
        self.segfunc = segfunc
        self.ide_ele = ide_ele
        self.num = 1 << (n - 1).bit_length()
        self.tree = [ide_ele] * 2 * self.num
        # 配列の値を葉にセット
        for i in range(n):
            self.tree[self.num + i] = init_val[i]
        # 構築していく
        for i in range(self.num - 1, 0, -1):
            self.tree[i] = self.segfunc(self.tree[2 * i], self.tree[2 * i + 1])

    def update(self, k, x):
        """
        k番目の値をxに更新
        k: index(0-index)
        x: update value
        """
        k += self.num
        self.tree[k] = x
        while k > 1:
            self.tree[k >> 1] = self.segfunc(self.tree[k], self.tree[k ^ 1])
            k >>= 1

    def query(self, l, r):
        """
        [l, r)のsegfuncしたものを得る
        l: index(0-index)
        r: index(0-index)
        """
        res = self.ide_ele

        l += self.num
        r += self.num
        while l < r:
            if l & 1:
                res = self.segfunc(res, self.tree[l])
                l += 1
            if r & 1:
                res = self.segfunc(res, self.tree[r - 1])
            l >>= 1
            r >>= 1
        return res


n = int(input())
s = input()
q = int(input())

ls = []

for ch in s:
    ls.append(1 << (ord(ch) - 97))

seg = SegTree(ls, segfunc, ide_ele)

for i in range(q):
    que = input().split()
    if que[0] == '1':
        iq = int(que[1])
        cq = que[2]
        seg.update(iq-1, 1 << (ord(cq) - 97))
    else:
        lq = int(que[1])
        rq = int(que[2])
        ret = seg.query(lq-1, rq)
        print((str(bin(ret)).count('1')))


from collections import defaultdict
from bisect import bisect_left, bisect_right, insort_left, insort_right
import sys
input = sys.stdin.readline


def main():
    N = int(input())
    S = list(input())
    q = int(input())
    Q = [input() for _ in range(q)]

    dd = defaultdict(list)  # 各アルファベットが出現するインデックスをddに登録
    for i in range(ord('a'), ord('z') + 1):
        dd[chr(i)] = []
    for i, st in enumerate(S):
        dd[st].append(i)

    ans = []
    for q in Q:
        t, a, b = q.split()
        a = int(a) - 1
        if int(t) == 1:
            if S[a] == b: continue
            # 二分探索で該当するインデックスを取得
            id = bisect_left(dd[S[a]], a)
            dd[S[a]].pop(id)  # 取ってくる
            insort_left(dd[b], a)  # 新規に挿入する
            S[a] = b
        else:
            b = int(b) - 1
            cnt = 0
            for l in dd.values():
                # liの中身が存在し，
                # そのアルファベットの最後のインデックスが指定されている最初のindexよりも大きく．
                # a以上の最初のindexがb以下の時は，そのアルファベットの存在は保証される
                if l and a <= l[-1] and l[bisect_left(l, a)] <= b:
                    cnt += 1
            ans.append(cnt)
    print(*ans, sep='\n')

def __starting_point():
    main()
__starting_point()

from bisect import bisect_left,insort_left
n=int(input())
s=list(input())
lindex=[[] for _ in range(26)]
for i in range(n):
  lindex[ord(s[i])-97].append(i)
q=int(input())
for i in range(q):
  q1,q2,q3=input().split()
  if q1=='1':
    q2=int(q2)-1
    idx=bisect_left(lindex[ord(s[q2])-97],q2)
    if s[q2]!=q3:
      del lindex[ord(s[q2])-97][idx]
      insort_left(lindex[ord(q3)-97],q2)
      s[q2]=q3
  else:
    l=int(q2)-1
    r=int(q3)-1
    cnt=0
    for j in range(26):
      if len(lindex[j])==0:
        continue
      idx=bisect_left(lindex[j],l)
      if len(lindex[j])>idx and lindex[j][idx]<=r:
        cnt+=1
    print(cnt)

from bisect import insort_left, bisect_left
n = int(input())
s = list(input())
Q = int(input())
query = [input() for _ in range(Q)]
dic = {chr(i+ord('a')):[] for i in range(26)}
for ind, c in enumerate(s):
    dic[c].append(ind)

for q in query:
    t, a, b = q.split()
    a = int(a)-1
    if t=='1':
        if s[a]==b:
            continue
        ind = bisect_left(dic[s[a]], a)
        dic[s[a]].pop(ind)
        insort_left(dic[b], a)
        s[a] = b
    else:
        b = int(b)-1
        tmp = 0
        for li in list(dic.values()):
            if li and a<=li[-1] and li[bisect_left(li, a)] <= b:
                tmp += 1
        print(tmp)


import bisect
n = int(input().strip())
S = list(input().strip())
L=[[] for _ in range(26)]
for i,s in enumerate(S):
    L[ord(s)-ord("a")].append(i)

q = int(input().strip())
for _ in range(q):
    query=input().strip().split()
    if query[0]=="1":
        i=int(query[1])
        i-=1
        c=query[2]
        if S[i]!=c:
            delInd=bisect.bisect_left(L[ord(S[i])-ord("a")],i)
            del L[ord(S[i])-ord("a")][delInd]
            bisect.insort(L[ord(c)-ord("a")], i)
            S[i]=c
    else:
        l,r=map(int,[query[1],query[2]])
        l-=1; r-=1
        ans=0
        for j in range(26):
            ind=bisect.bisect_left(L[j],l)
            if ind<len(L[j]) and L[j][ind]<=r:
                ans+=1
        print(ans)

#!python3

import sys

input = sys.stdin.readline

def resolve():
    N = int(input())
    S = list(input())
    Q = int(input())

    c0 = ord('a')
    smap = [1<<(i-c0) for i in range(c0, ord('z')+1)]

    T = [0]*N + [smap[ord(S[i])-c0] for i in range(N)]

    for i in range(N-1, 0, -1):
        i2 = i << 1
        T[i] = T[i2] | T[i2|1]

    ans = []
    #print(T)
    for cmd, i, j in zip(*[iter(sys.stdin.read().split())]*3):
        i = int(i) - 1
        if cmd == "1":
            if S[i] == j:
                continue

            S[i] = j
            i0 = N + i
            T[i0] = smap[ord(j)-c0]
            while i0 > 1:
                i0 = i0 >> 1
                T[i0] = T[i0+i0] | T[i0-~i0]
        elif cmd == "2":
            i += N
            j = int(j) + N

            d1 = 0
            while i < j:
                if i & 1:
                    d1 |= T[i]
                    i += 1
                if j & 1:
                    j -= 1
                    d1 |= T[j]

                i >>= 1; j >>=1

            ans.append(bin(d1).count('1'))

    print(*ans, sep="\n")

def __starting_point():
    resolve()

__starting_point()

class SegmentTree():
    '''
    非再帰
    segment tree
    '''

    def __init__(self, n, func, init=float('inf')):
        '''
        n->配列の長さ
        func:func(a,b)->val,　func=minだとRMQになる
        木の高さhとすると,
        n:h-1までのノード数。h段目のノードにアクセスするために使う。
        data:ノード。
        parent:k->child k*2+1とk*2+2
        '''
        self.n = 2**(n-1).bit_length()
        self.init = init
        self.data = [init]*(2*self.n)
        self.func = func

    def set(self, k, v):
        '''
        あたいの初期化
        '''
        self.data[k+self.n-1] = v

    def build(self):
        '''
        setの後に一斉更新
        '''
        for k in reversed(list(range(self.n-1))):
            self.data[k] = self.func(self.data[k*2+1], self.data[k*2+2])

    def update(self, k, a):
        '''
        list[k]=aに更新する。
        更新ぶんをrootまで更新
        '''
        k += self.n-1
        self.data[k] = a

        while k > 0:
            k = (k-1)//2
            self.data[k] = self.func(self.data[k*2+1], self.data[k*2+2])

    def query(self, l, r):
        '''
        [l,r)のfuncを求める
        '''
        L = l+self.n
        R = r+self.n
        ret = self.init
        while L < R:
            if R & 1:
                R -= 1
                ret = self.func(ret, self.data[R-1])
            if L & 1:
                ret = self.func(ret, self.data[L-1])
                L += 1
            L >>= 1
            R >>= 1
        return ret


N = int(input())
S = input()
Q = int(input())
queries = [list(input().split()) for _ in range(Q)]


def lam(x, y):
    return x | y


def a2n(a):
    return ord(a)-ord("a")


def createInp(n):
    return 1 << n


Seg = SegmentTree(len(S), lam, init=0)
for i, s in enumerate(S):
    Seg.set(i, createInp(a2n(s)))
Seg.build()

for q, l, r in queries:
    if q == "1":
        Seg.update(int(l)-1, createInp(a2n(r)))
    else:
        print((bin(Seg.query(int(l)-1, int(r))).count('1')))


class Segment_tree():
    def __init__(self, n, s):
        self.alphabet_list = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"]
        self.key = dict()
        self.k = 1
        for value in self.alphabet_list:
            self.key[value] = self.k
            self.k *= 2
        self.inf = pow(2, 32)
        self.n = n
        self.s = s
        self.N = 1
        while self.N < self.n:
            self.N *= 2
        self.size = self.N * 2
        self.size -= 1
        self.N -= 1
        self.segment = [0]*(self.N) + [self.key[self.s[i]] for i in range(self.n)] + [0]*(self.size-self.N-self.n)
        for i in range(self.N-1, -1, -1):
            self.segment[i] = self.segment[2*i+1]|self.segment[2*i+2]

    def update(self, i, x):
        i += (self.N-1)
        self.segment[i] = self.key[x]
        while i > 0:
            i = (i-1)//2
            self.segment[i] = self.segment[2*i+1]|self.segment[2*i+2]
        
    def find(self, a, b, k, l, r):
        if r <= a or b <= l:
            return 0
        elif a <= l and r <= b:
            return self.segment[k]
        else:
            find_l = self.find(a, b, 2*k+1, l, (l+r)//2)
            find_r = self.find(a, b, 2*k+2, (l+r)//2, r)
            return find_l|find_r


n = int(input())
s = input()
segment = Segment_tree(n, s)
q = int(input())
N = 1
while N < n:
  N *= 2
for i in range(q):
    quer = [x for x in input().split()]
    if quer[0] == "1":
        i, c = int(quer[1]), quer[2]
        segment.update(i, c)
    else:
        l, r = int(quer[1]), int(quer[2])
        key = segment.find(l-1, r, 0, 0, N)
        ans = 0
        for i in range(26):
            if (key>>i)&1:
                ans += 1
        print(ans)

n = int(input())
s = list(input())

dic = {}
for i in range(26):
    dic[chr(i+97)] = []

for i in range(n):
    dic[s[i]].append(i)

for i in range(26):
    dic[chr(i+97)].append(float('inf'))

from bisect import bisect_left
q = int(input())
for i in range(q):
    x, y, z = input().split()
    if x == '1':
        y = int(y) - 1
        p = bisect_left(dic[s[y]], y)
        dic[s[y]].pop(p)
        dic[z].insert(bisect_left(dic[z], y), y)
        s[y] = z
    else:
        res = 0
        y, z = int(y) - 1, int(z) - 1
        for i in range(26):
            p = dic[chr(i+97)][bisect_left(dic[chr(i+97)], y)]
            if p <= z:
                res += 1
        print(res)

class SegTree:
    def __init__(self, N, S):
        self.N = 2 ** ((N-1).bit_length())
        self.tree = [0 for i in range(self.N * 2 - 1)]
        for i in range(len(S)):
            self.tree[self.N - 1 + i] = 1 << (ord(S[i]) - ord("a"))

        for i in range(self.N - 2, -1, -1):
            self.tree[i] = self.tree[i*2 + 1] | self.tree[i*2 + 2]

    def update(self, index, char):
        i = self.N - 1 + index
        # アルファベットがある = bitが立っている
        self.tree[i] = 1 << (ord(char) - ord("a"))
        while i > 0:
            i = (i-1) // 2
            self.tree[i] = self.tree[i*2 + 1] | self.tree[i*2 + 2]

    def get(self, a, b):
        return self.query(a, b, 0, 0, self.N)

    # k : ノードの番号
    # l : ノードkの受け持つ区間
    # r : ノードkの受け持つ区間
    def query(self, a, b, k, l, r):
        if r <= a or b <= l: # 区間外
            return 0
        elif a <= l and r <= b: # 受け持ち区間は完全に含まれている
            return self.tree[k]
        else: # 一部含まれてる
            left = self.query(a, b, 2 * k + 1, l, (l+r)//2)
            right = self.query(a, b, 2 * k + 2, (l+r)//2, r)
            return left | right

def solve():
    N = int(input())
    S = list(input().strip())
    tree = SegTree(N, S)

    Q = int(input())
    for _ in range(Q):
        t_q, in_1, in_2 = input().split()
        if t_q == "1":
            i_q = int(in_1)
            c_q = in_2
            tree.update(i_q-1, c_q)
        else:
            l_q = int(in_1)
            r_q = int(in_2)
            print(bin(tree.get(l_q-1, r_q)).count("1"))

solve()

import sys
def input(): return sys.stdin.readline().rstrip()


class SegTree:
    X_unit = 0

    def __init__(self, N, X_f=min):
        self.N = N
        self.X = [self.X_unit] * (N + N)
        self.X_f = X_f

    def build(self, seq):
        for i, x in enumerate(seq, self.N):
            self.X[i] = x
        for i in range(self.N - 1, 0, -1):
            self.X[i] = self.X_f(self.X[i << 1], self.X[i << 1 | 1])

    def set_val(self, i, x):
        i += self.N
        self.X[i] = x
        while i > 1:
            i >>= 1
            self.X[i] = self.X_f(self.X[i << 1], self.X[i << 1 | 1])

    def fold(self, L, R):
        L += self.N
        R += self.N
        vL = self.X_unit
        vR = self.X_unit
        while L < R:
            if L & 1:
                vL = self.X_f(vL, self.X[L])
                L += 1
            if R & 1:
                R -= 1
                vR = self.X_f(self.X[R], vR)
            L >>= 1
            R >>= 1
        return self.X_f(vL, vR)


def orr(x, y):
    return x | y


def main():
    N = int(input())
    S = input()
    S = list(map(lambda c: 2**(ord(c) - ord('a')), list(S)))
    Q = int(input())
    seg = SegTree(N, orr)
    seg.build(S)
    for _ in range(Q):
        num, x, y = input().split()
        if num == '1':
            seg.set_val(int(x)-1, 2**(ord(y) - ord('a')))
        else:
            bits=seg.fold(int(x)-1, int(y))
            print(sum(map(int, list(bin(bits))[2:])))


def __starting_point():
    main()
__starting_point()

class SegmentTree(object):
    def __init__(self, sequence, function, identify):
        N = len(sequence)
        self.length = (1 << (N - 1)).bit_length()
        self.function = function
        self.data = [identify] * (self.length << 1)
        self.identify = identify
        # set values
        for i in range(N):
            self.data[i + self.length - 1] = sequence[i]
        # build
        for i in range(self.length - 2, -1, -1) :
            self.data[i] = self.function(self.data[(i << 1) + 1], self.data[(i << 1) + 2])

    def update(self, idx, x):
        idx += self.length - 1
        self.data[idx] = x
        while idx:
            idx = (idx - 1) >> 1
            self.data[idx] = self.function(self.data[(idx << 1) + 1], self.data[(idx << 1) + 2])

    def query(self, p, q):
        if q <= p:
            return self.identify
        p += self.length - 1
        q += self.length - 2
        res = self.identify
        while q - p > 1:
            if not p & 1:
                res = self.function(res, self.data[p])
            if q & 1:
                res = self.function(res, self.data[q])
                q -= 1
            p >>= 1
            q = (q - 1) >> 1
        return self.function(res, self.data[p]) if p == q else self.function(self.function(res, self.data[p]), self.data[q])

def popcount(x):
    x = x - ((x >> 1) & 0x5555555555555555)
    x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333)
    x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0f
    x = x + (x >> 8)
    x = x + (x >> 16)
    x = x + (x >> 32)
    return x & 0x0000007f


N = int(input())
S = input()
atoi = {chr(ord("a") + d): 1 << d for d in range(26)}
data = [atoi[c] for c in (S)]
seg_tree = SegmentTree(data, lambda a, b: a | b, 0)
Q = int(input())
for _ in range(Q):
    q, a, b = input().split()
    if q == "1":
        idx = int(a) - 1
        seg_tree.update(idx, atoi[b])
    else:
        print(popcount(seg_tree.query(int(a) - 1, int(b))))

from bisect import bisect_left,bisect_right,insort

n = int(input())
s = list("-" + str(input()))
lis = [[] for i in range(26)]
for i in range(1,n+1):
    num = ord(s[i])-ord("a")
    lis[num].append(i)

q = int(input())
for turn in range(q):
    query = list(map(str,input().split()))
    if query[0] == "1":
        a = int(query[1])
        if s[a] == query[2]:
            continue
        num = ord(s[a])-ord("a")
        lis[num].pop(bisect_left(lis[num],a))
        next = ord(query[2])-ord("a")
        insort(lis[next],a)
        s[a] = query[2]
    else:
        a,b = int(query[1]),int(query[2])
        ans = 0
        for alphabet in range(26):
            if bisect_right(lis[alphabet],b)-bisect_left(lis[alphabet],a) >= 1:
                ans += 1
        print(ans)

# coding: utf-8
import sys
#from operator import itemgetter
sysread = sys.stdin.readline
#from heapq import heappop, heappush
from collections import defaultdict
#from itertools import combinations
sys.setrecursionlimit(10**7)
import math
class SegTree():
    '''
    input idx: 1-
    '''
    def __init__(self, n, init_val=0):
        self.n = n
        self.init_val = init_val
        self.n_leaf = 2 ** math.ceil(math.log2(n))
        self.bins = [init_val] * (self.n_leaf * 2)

    def val_append(self, A):
        '''
        :param A:
        :return:
        '''
        if len(A) != self.n:
            raise ValueError('The number of input should be same with n_leaf')
        for i,a in enumerate(A):
            self.bins[self.n_leaf+i]=a
        self._calc()
        return None

    def _calc(self):
        '''
        Calculate all bins based on values of leaves
        '''
        i = self.n_leaf - 1
        while i > 0:
            self.bins[i] = self._criteria(self.bins[i*2], self.bins[i*2+1])
            i -= 1
        return None

    def _criteria(self, x, y):
        '''
        Define criteria between 2 nodes
        '''
        return x | y

    def eval_between(self, x, y):
        '''
        x, y : node-idx(1-)
        '''
        l, r = x + self.n_leaf - 1, y + self.n_leaf - 1
        ret = self._criteria(self.bins[l], self.bins[r])
        while l < r:
            if l % 2 == 1:
                ret = self._criteria(self.bins[l], ret)
                l += 1
                continue

            if r % 2 == 0:
                ret = self._criteria(self.bins[r], ret)
                r -= 1
                continue
            l = l//2
            r = r//2
            ret = self._criteria(self.bins[l], ret)
            ret = self._criteria(self.bins[r], ret)

        return ret

    def update(self, node, pro):
        '''
        Replacement of the value in a leaf
        '''
        current = node + self.n_leaf - 1
        self.bins[current] = pro
        current //= 2
        while current > 0:
            self.bins[current] = self._criteria(self.bins[current*2+1], self.bins[current*2])
            current //= 2
        return None

def run():
    N = int(input())
    S = list(input())
    Q = int(input())
    queries = [sysread().split() for _ in range(Q)]

    bins = [0] * N# a: ord('a') - 97
    for i,s in enumerate(S):
        id = ord(s) - 97
        bins[i] = 1<<id

    segtree = SegTree(N)
    segtree.val_append(bins)

    for type, i,c in queries:
        if type == '1':
            c = 1 << (ord(c) - 97)
            segtree.update(int(i), c)
        else:
            i,c = int(i), int(c)
            tmp = segtree.eval_between(i,c)
            print(bin(tmp).count('1'))

def __starting_point():
    run()
__starting_point()

# Segment tree (Bitwise Or)
from operator import or_


class SegmentTree:
    def __init__(self, size, op, e):
        self._op = op
        self._e = e
        self._size = size
        t = 1
        while t < size:
            t *= 2
        self._offset = t - 1
        self._data = [e] * (t * 2 - 1)

    def __getitem__(self, key):
        return self._data[self._offset + key]

    def __setitem__(self, key, value):
        op = self._op
        data = self._data
        i = self._offset + key
        data[i] = value
        while i >= 1:
            i = (i - 1) // 2
            data[i] = op(data[i * 2 + 1], data[i * 2 + 2])

    def build(self, iterable):
        op = self._op
        data = self._data
        data[self._offset:self._offset + self._size] = iterable
        for i in range(self._offset - 1, -1, -1):
            data[i] = op(data[i * 2 + 1], data[i * 2 + 2])

    def query(self, start, stop):
        def iter_segments(data, l, r):
            while l < r:
                if l & 1 == 0:
                    yield data[l]
                if r & 1 == 0:
                    yield data[r - 1]
                l = l // 2
                r = (r - 1) // 2
        op = self._op
        it = iter_segments(self._data, start + self._offset,
                           stop + self._offset)
        result = self._e
        for v in it:
            result = op(result, v)
        return result


def conv(c):
    return 1 << (ord(c) - ord('a'))


N = int(input())
S = input()

st = SegmentTree(N, or_, 0)
st.build(conv(c) for c in S)

Q = int(input())
for _ in range(Q):
    q = input().split()
    if q[0] == '1':
        i, c = q[1:]
        i = int(i) - 1
        st[i] = conv(c)
    elif q[0] == '2':
        l, r = list(map(int, q[1:]))
        print((bin(st.query(l - 1, r)).count('1')))


import bisect  # 配列二分法
from collections import defaultdict

N = int(input())
S = [*input()]
Q = int(input())
d = defaultdict(list)

# 各アルファベットの出現場所を記録
for i, c in enumerate(S):
    d[c] += [i]

# この時点でどのアルファベットについてもソート済み
# print(d)

for _ in range(Q):
    q, y, z = input().split()

    if q == '1':
        i = int(y) - 1  # 文字の変更位置

        if S[i] == z: continue  # 同じ文字の場合は処理終了

        # 削除 O(N)
        b = bisect.bisect(d[S[i]], i)  # 2分法で i の出現位置を(log N)で探す
        d[S[i]].pop(b - 1)  # 削除

        # 追加 O(N) かかる？
        S[i] = z
        bisect.insort(d[z], i) 

    else:
        left = int(y) - 1
        right = int(z) - 1

        count = 0
        for v in d.values():
            count += 1 if bisect.bisect(v, right) - bisect.bisect_left(v, left) > 0 else 0

        print(count)

def main():
    from bisect import bisect_left,bisect
    n = int(input())
    s = list(input())
    q = int(input())
    al=[[] for i in range(26)]
    for i in range(n):
        al[ord(s[i])-97].append(i)
    ans = []
    for i in range(q):
        a , b , x = input().split()
        if a == "1":
            al[ord(s[int(b)-1])-97].pop(bisect_left(al[ord(s[int(b)-1])-97],int(b)-1))
            al[ord(x)-97].insert(bisect_left(al[ord(x)-97],int(b)-1),int(b)-1)
            s[int(b)-1] = x
        elif a == "2":
            b = int(b)-1
            x = int(x)-1
            cou = 0
            for j in range(26):
                l = bisect_left(al[j],b)
                r = bisect(al[j],x)
                if l != r:
                    cou += 1
            ans.append(cou)
    for i in ans:
        print(i)

def __starting_point():
    main()
__starting_point()

import bisect
import sys
def input(): return sys.stdin.readline().rstrip()

n = int(input())
s = list(input())
q = int(input())

char_idx = [[] for _ in range(26)]
for i in range(n):
    char_idx[ord(s[i])-ord('a')].append(i)
# print(char_idx)

query = [input().split() for _ in range(q)]
# print(query)

for t, a, b in query:
    if t=='1':
        i = int(a) - 1
        if s[i] == b:
            continue
        else:
            idx = bisect.bisect_left(char_idx[ord(s[i])-ord('a')], i)
            if char_idx[ord(s[i])-ord('a')][idx] == i:
                del char_idx[ord(s[i])-ord('a')][idx]
            bisect.insort_left(char_idx[ord(b)-ord('a')], i)
            s[i] = b
    else:
        l = int(a) - 1
        r = int(b) - 1
        ans = 0
        for i in range(26):
            if len(char_idx[i]) == 0:
                continue
            it = bisect.bisect_left(char_idx[i], l)
            if it < len(char_idx[i]) and char_idx[i][it] <= r:
                ans += 1
        print(ans)


from bisect import bisect_left, insort_left
N = int(input())
S = [c for c in input()]
Q = int(input())
d = {i: [] for i in range(26)}
for i, c in enumerate(S):
    d[ord(c) - ord("a")].append(i)

# print(d)
for _ in range(Q):
    q = input().split()
    if int(q[0]) == 1:
        iq, cq = int(q[1]), q[2]
        iq -= 1
        if S[iq] == cq:
            continue
        else:
            idx = bisect_left(d[ord(S[iq]) - ord("a")], iq)
            d[ord(S[iq]) - ord("a")].pop(idx)
            S[iq] = cq
            insort_left(d[ord(cq) - ord("a")], iq)
    else:
        l, r = int(q[1]), int(q[2])
        l -= 1
        ans = 0
        for i in range(26):
            idx = bisect_left(d[i], l)
            if d[i] and l <= d[i][-1] and d[i][idx] < r:
                ans += 1
        print(ans)