class SegmentTree:
    # 初期化処理
    # f : SegmentTreeにのせるモノイド
    # default : fに対する単位元
    def __init__(self, size, f=lambda x,y : x+y, default=0):
        self.size = 2**(size-1).bit_length() # 簡単のため要素数Nを2冪にする
        self.default = default
        self.dat = [default]*(self.size*2) # 要素を単位元で初期化
        self.f = f

    # 1-indexled なので注意
    def update(self, i, x):
        i += self.size
        self.dat[i] = x
        while i > 0:
            i >>= 1
            self.dat[i] = self.f(self.dat[i*2], self.dat[i*2+1])

    # 1-indexled なので注意
    def query(self, l, r):
        l += self.size
        r += self.size
        lres, rres = self.default, self.default
        while l < r:
            if l & 1:
                lres = self.f(lres, self.dat[l])
                l += 1

            if r & 1:
                r -= 1
                rres = self.f(self.dat[r], rres) # モノイドでは可換律は保証されていないので演算の方向に注意
            l >>= 1
            r >>= 1
        res = self.f(lres, rres)
        return res

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

import pysnooper

#@pysnooper.snoop()
def main():
    N = int(input())
    S = input()
    Q = int(input())
    ST = SegmentTree(N, f=f, default=set([]))

    for i, s in enumerate(S):
        ST.update(i+1, set([s]))

    for _ in range(Q):
        t, l, r = input().split()
        if int(t) == 1:
            ST.update(int(l), set([r]))
        else:
            print(len(ST.query(int(l), int(r)+1)))

if __name__ == '__main__':
    main()


### FIXED CODE ###

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(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'))