#!usr/bin/env python3
from collections import defaultdict, deque, Counter, OrderedDict
from bisect import bisect_left, bisect_right
from functools import reduce, lru_cache
from heapq import heappush, heappop, heapify

import itertools, bisect
import math, fractions
import sys, copy

def L(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline().rstrip())
def S(): return list(sys.stdin.readline().rstrip())
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()]
def LS(): return [list(x) for x in sys.stdin.readline().split()]
def IR(n): return [I() for _ in range(n)]
def LIR(n): return [LI() for _ in range(n)]
def LIR1(n): return [LI1() for _ in range(n)]
def SR(n): return [S() for _ in range(n)]
def LSR(n): return [LS() for _ in range(n)]
def LR(n): return [L() for _ in range(n)]

alphabets = "abcdefghijklmnopqrstuvwxyz"

sys.setrecursionlimit(1000000)
dire = [[1, 0], [0, 1], [-1, 0], [0, -1]]
dire8 = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]
MOD = 1000000007

# nodeをリストに変換したらクソ遅かった

import pprint

class BalancingTree:
    def __init__(self, n):
        self.N = n
        self.root = self.node(1<<n, 1<<n)

    def __str__(self):
        def debug_info(nd):
            return (nd.value - 1, nd.left.value - 1 if nd.left else None, nd.right.value - 1 if nd.right else None)

        def debug_node(nd):
            v = debug_info(nd) if nd.value else ()
            left = debug_node(nd.left) if nd.left else []
            right = debug_node(nd.right) if nd.right else []
            return [v, left, right]

        return pprint.PrettyPrinter(indent=4).pformat(debug_node(self.root))

    __repr__ = __str__

    def append(self, v):# v を追加（その時点で v はない前提）
        v += 1
        nd = self.root
        while True:
            # v がすでに存在する場合に何か処理が必要ならここに書く
            if v == nd.value:
                return 0
            else:
                mi, ma = min(v, nd.value), max(v, nd.value)
                if mi < nd.pivot:
                    nd.value = ma
                    if nd.left:
                        nd = nd.left
                        v = mi
                    else:
                        p = nd.pivot
                        nd.left = self.node(mi, p - (p&-p)//2)
                        break
                else:
                    nd.value = mi
                    if nd.right:
                        nd = nd.right
                        v = ma
                    else:
                        p = nd.pivot
                        nd.right = self.node(ma, p + (p&-p)//2)
                        break

    def leftmost(self, nd):
        return self.leftmost(nd.left) if nd.left else nd

    def rightmost(self, nd):
        return self.rightmost(nd.right) if nd.right else nd

    def find_l(self, v): # vより真に小さいやつの中での最大値（なければ-1）
        v += 1
        nd = self.root
        prev = nd.value if nd.value < v else 0
        while True:
            if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                prev = nd.value
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

    def find_r(self, v): # vより真に大きいやつの中での最小値（なければRoot）
        v += 1
        nd = self.root
        prev = nd.value if nd.value > v else 0
        while True:
            if v < nd.value:
                prev = nd.value
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

    def max(self):
        return self.find_l((1<<self.N)-1)

    def min(self):
        return self.find_r(-1)

    def delete(self, v, nd = None, prev = None): # 値がvのノードがあれば削除（なければ何もしない）
        v += 1
        if not nd:
            nd = self.root
        if not prev:
            prev = nd
        while v != nd.value:
            prev = nd
            if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return
        if (not nd.left) and (not nd.right):
            if nd.value < prev.value:
                prev.left = None
            else:
                prev.right = None
        elif not nd.left:
            if nd.value < prev.value:
                prev.left = nd.right
            else:
                prev.right = nd.right
        elif not nd.right:
            if nd.value < prev.value:
                prev.left = nd.left
            else:
                prev.right = nd.left
        else:
            nd.value = self.leftmost(nd.right).value
            self.delete(nd.value - 1, nd.right, nd)

    # v以下のものの中での最大値
    def upper_bound(self, v):
        upper = self.find_r(v)
        return self.find_l(upper)

    def lower_bound(self, v):
        lower = self.find_l(v)
        return self.find_r(lower)

    def __contains__(self, v: int) -> bool:
        return self.find_r(v - 1) == v

    class node:
        def __init__(self, v, p):
            self.value = v
            self.pivot = p
            self.left = None
            self.right = None

def main():
    N = I()
    s = S()
    Q = I()
    query = LR(Q)

    trees = {ch: BalancingTree(25) for ch in alphabets}

    for i, ch in enumerate(s):
        trees[ch].append(i)

    for q in query:
        if q[0] == "1":
            _, i, c = q
            i = int(i) - 1
            prev = s[i]
            trees[prev].delete(i)
            s[i] = c
            trees[c].append(i)
        else:
            _, l, r = q
            l, r = int(l) - 1, int(r) - 1
            count = 0
            for _, tree in trees.items():
                first = tree.lower_bound(l)
                if first != tree.root.value - 1 and first <= r:
                    count += 1
            print(count)



if __name__ == '__main__':
    main()
 from collections import defaultdict, deque, Counter, OrderedDict from bisect import bisect_left, bisect_right from functools import reduce, lru_cache from heapq import heappush, heappop, heapify import itertools, bisect itertools bisect import math, fractions math fractions import sys, copy sys copy def L(): return sys.stdin.readline().split() L def I(): return int(sys.stdin.readline().rstrip()) I def S(): return list(sys.stdin.readline().rstrip()) S int(x) for x in sys.stdin.readline().split() for x in sys.stdin.readline().split() x sys.stdin.readline().split() sys.stdin.readline().split sys.stdin.readline() sys.stdin.readline sys.stdin sys stdin readline split for x in sys.stdin.readline().split() int(x) int x def LI(): return [int(x) for x in sys.stdin.readline().split()] LI int(x) - 1 for x in sys.stdin.readline().split() for x in sys.stdin.readline().split() x sys.stdin.readline().split() sys.stdin.readline().split sys.stdin.readline() sys.stdin.readline sys.stdin sys stdin readline split for x in sys.stdin.readline().split() int(x) - 1 int(x) int x 1 def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()] LI1 list(x) for x in sys.stdin.readline().split() for x in sys.stdin.readline().split() x sys.stdin.readline().split() sys.stdin.readline().split sys.stdin.readline() sys.stdin.readline sys.stdin sys stdin readline split for x in sys.stdin.readline().split() list(x) list x def LS(): return [list(x) for x in sys.stdin.readline().split()] LS I() for _ in range(n) for _ in range(n) _ range(n) range n for _ in range(n) I() I def IR(n): return [I() for _ in range(n)] IR n n LI() for _ in range(n) for _ in range(n) _ range(n) range n for _ in range(n) LI() LI def LIR(n): return [LI() for _ in range(n)] LIR n n LI1() for _ in range(n) for _ in range(n) _ range(n) range n for _ in range(n) LI1() LI1 def LIR1(n): return [LI1() for _ in range(n)] LIR1 n n S() for _ in range(n) for _ in range(n) _ range(n) range n for _ in range(n) S() S def SR(n): return [S() for _ in range(n)] SR n n LS() for _ in range(n) for _ in range(n) _ range(n) range n for _ in range(n) LS() LS def LSR(n): return [LS() for _ in range(n)] LSR n n L() for _ in range(n) for _ in range(n) _ range(n) range n for _ in range(n) L() L def LR(n): return [L() for _ in range(n)] LR n n alphabets = "abcdefghijklmnopqrstuvwxyz" alphabets "abcdefghijklmnopqrstuvwxyz" sys.setrecursionlimit(1000000) sys.setrecursionlimit sys setrecursionlimit 1000000 dire = [[1, 0], [0, 1], [-1, 0], [0, -1]] dire [[1, 0], [0, 1], [-1, 0], [0, -1]] [1, 0] 1 0 [0, 1] 0 1 [-1, 0] -1 0 [0, -1] 0 -1 dire8 = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]] dire8 [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]] [1, 0] 1 0 [1, 1] 1 1 [0, 1] 0 1 [-1, 1] -1 1 [-1, 0] -1 0 [-1, -1] -1 -1 [0, -1] 0 -1 [1, -1] 1 -1 MOD = 1000000007 MOD 1000000007 import pprint pprint class BalancingTree:
    def __init__(self, n):
        self.N = n
        self.root = self.node(1<<n, 1<<n)

    def __str__(self):
        def debug_info(nd):
            return (nd.value - 1, nd.left.value - 1 if nd.left else None, nd.right.value - 1 if nd.right else None)

        def debug_node(nd):
            v = debug_info(nd) if nd.value else ()
            left = debug_node(nd.left) if nd.left else []
            right = debug_node(nd.right) if nd.right else []
            return [v, left, right]

        return pprint.PrettyPrinter(indent=4).pformat(debug_node(self.root))

    __repr__ = __str__

    def append(self, v):# v を追加（その時点で v はない前提）
        v += 1
        nd = self.root
        while True:
            # v がすでに存在する場合に何か処理が必要ならここに書く
            if v == nd.value:
                return 0
            else:
                mi, ma = min(v, nd.value), max(v, nd.value)
                if mi < nd.pivot:
                    nd.value = ma
                    if nd.left:
                        nd = nd.left
                        v = mi
                    else:
                        p = nd.pivot
                        nd.left = self.node(mi, p - (p&-p)//2)
                        break
                else:
                    nd.value = mi
                    if nd.right:
                        nd = nd.right
                        v = ma
                    else:
                        p = nd.pivot
                        nd.right = self.node(ma, p + (p&-p)//2)
                        break

    def leftmost(self, nd):
        return self.leftmost(nd.left) if nd.left else nd

    def rightmost(self, nd):
        return self.rightmost(nd.right) if nd.right else nd

    def find_l(self, v): # vより真に小さいやつの中での最大値（なければ-1）
        v += 1
        nd = self.root
        prev = nd.value if nd.value < v else 0
        while True:
            if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                prev = nd.value
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

    def find_r(self, v): # vより真に大きいやつの中での最小値（なければRoot）
        v += 1
        nd = self.root
        prev = nd.value if nd.value > v else 0
        while True:
            if v < nd.value:
                prev = nd.value
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

    def max(self):
        return self.find_l((1<<self.N)-1)

    def min(self):
        return self.find_r(-1)

    def delete(self, v, nd = None, prev = None): # 値がvのノードがあれば削除（なければ何もしない）
        v += 1
        if not nd:
            nd = self.root
        if not prev:
            prev = nd
        while v != nd.value:
            prev = nd
            if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return
        if (not nd.left) and (not nd.right):
            if nd.value < prev.value:
                prev.left = None
            else:
                prev.right = None
        elif not nd.left:
            if nd.value < prev.value:
                prev.left = nd.right
            else:
                prev.right = nd.right
        elif not nd.right:
            if nd.value < prev.value:
                prev.left = nd.left
            else:
                prev.right = nd.left
        else:
            nd.value = self.leftmost(nd.right).value
            self.delete(nd.value - 1, nd.right, nd)

    # v以下のものの中での最大値
    def upper_bound(self, v):
        upper = self.find_r(v)
        return self.find_l(upper)

    def lower_bound(self, v):
        lower = self.find_l(v)
        return self.find_r(lower)

    def __contains__(self, v: int) -> bool:
        return self.find_r(v - 1) == v

    class node:
        def __init__(self, v, p):
            self.value = v
            self.pivot = p
            self.left = None
            self.right = None BalancingTree def __init__(self, n):
        self.N = n
        self.root = self.node(1<<n, 1<<n)

     __init__ self.N = n self.N self N n self.root = self.node(1<<n, 1<<n) self.root self root self.node(1<<n, 1<<n) self.node self node 1<<n 1 n 1<<n 1 n self self n n def __str__(self):
        def debug_info(nd):
            return (nd.value - 1, nd.left.value - 1 if nd.left else None, nd.right.value - 1 if nd.right else None)

        def debug_node(nd):
            v = debug_info(nd) if nd.value else ()
            left = debug_node(nd.left) if nd.left else []
            right = debug_node(nd.right) if nd.right else []
            return [v, left, right]

        return pprint.PrettyPrinter(indent=4).pformat(debug_node(self.root))

     __str__ def debug_info(nd):
            return (nd.value - 1, nd.left.value - 1 if nd.left else None, nd.right.value - 1 if nd.right else None)

         debug_info return (nd.value - 1, nd.left.value - 1 if nd.left else None, nd.right.value - 1 if nd.right else None) (nd.value - 1, nd.left.value - 1 if nd.left else None, nd.right.value - 1 if nd.right else None) nd.value - 1 nd.value nd value 1 nd.left.value - 1 if nd.left else None nd.left nd left nd.left.value - 1 nd.left.value nd.left nd left value 1 None nd.right.value - 1 if nd.right else None nd.right nd right nd.right.value - 1 nd.right.value nd.right nd right value 1 None nd nd def debug_node(nd):
            v = debug_info(nd) if nd.value else ()
            left = debug_node(nd.left) if nd.left else []
            right = debug_node(nd.right) if nd.right else []
            return [v, left, right]

         debug_node v = debug_info(nd) if nd.value else () v debug_info(nd) if nd.value else () nd.value nd value debug_info(nd) debug_info nd () left = debug_node(nd.left) if nd.left else [] left debug_node(nd.left) if nd.left else [] nd.left nd left debug_node(nd.left) debug_node nd.left nd left [] right = debug_node(nd.right) if nd.right else [] right debug_node(nd.right) if nd.right else [] nd.right nd right debug_node(nd.right) debug_node nd.right nd right [] return [v, left, right] [v, left, right] v left right nd nd return pprint.PrettyPrinter(indent=4).pformat(debug_node(self.root)) pprint.PrettyPrinter(indent=4).pformat(debug_node(self.root)) pprint.PrettyPrinter(indent=4).pformat pprint.PrettyPrinter(indent=4) pprint.PrettyPrinter pprint PrettyPrinter indent=4 indent 4 pformat debug_node(self.root) debug_node self.root self root self self __repr__ = __str__ __repr__ __str__ def append(self, v):# v を追加（その時点で v はない前提）
        v += 1
        nd = self.root
        while True:
            # v がすでに存在する場合に何か処理が必要ならここに書く
            if v == nd.value:
                return 0
            else:
                mi, ma = min(v, nd.value), max(v, nd.value)
                if mi < nd.pivot:
                    nd.value = ma
                    if nd.left:
                        nd = nd.left
                        v = mi
                    else:
                        p = nd.pivot
                        nd.left = self.node(mi, p - (p&-p)//2)
                        break
                else:
                    nd.value = mi
                    if nd.right:
                        nd = nd.right
                        v = ma
                    else:
                        p = nd.pivot
                        nd.right = self.node(ma, p + (p&-p)//2)
                        break

     append v += 1 v 1 nd = self.root nd self.root self root while True:
            # v がすでに存在する場合に何か処理が必要ならここに書く
            if v == nd.value:
                return 0
            else:
                mi, ma = min(v, nd.value), max(v, nd.value)
                if mi < nd.pivot:
                    nd.value = ma
                    if nd.left:
                        nd = nd.left
                        v = mi
                    else:
                        p = nd.pivot
                        nd.left = self.node(mi, p - (p&-p)//2)
                        break
                else:
                    nd.value = mi
                    if nd.right:
                        nd = nd.right
                        v = ma
                    else:
                        p = nd.pivot
                        nd.right = self.node(ma, p + (p&-p)//2)
                        break

     True if v == nd.value:
                return 0
            else:
                mi, ma = min(v, nd.value), max(v, nd.value)
                if mi < nd.pivot:
                    nd.value = ma
                    if nd.left:
                        nd = nd.left
                        v = mi
                    else:
                        p = nd.pivot
                        nd.left = self.node(mi, p - (p&-p)//2)
                        break
                else:
                    nd.value = mi
                    if nd.right:
                        nd = nd.right
                        v = ma
                    else:
                        p = nd.pivot
                        nd.right = self.node(ma, p + (p&-p)//2)
                        break

     v == nd.value v nd.value nd value return 0 0 mi, ma = min(v, nd.value), max(v, nd.value) mi min(v, nd.value) min v nd.value nd value ma max(v, nd.value) max v nd.value nd value if mi < nd.pivot:
                    nd.value = ma
                    if nd.left:
                        nd = nd.left
                        v = mi
                    else:
                        p = nd.pivot
                        nd.left = self.node(mi, p - (p&-p)//2)
                        break
                else:
                    nd.value = mi
                    if nd.right:
                        nd = nd.right
                        v = ma
                    else:
                        p = nd.pivot
                        nd.right = self.node(ma, p + (p&-p)//2)
                        break

     mi < nd.pivot mi nd.pivot nd pivot nd.value = ma nd.value nd value ma if nd.left:
                        nd = nd.left
                        v = mi
                    else:
                        p = nd.pivot
                        nd.left = self.node(mi, p - (p&-p)//2)
                        break
                 nd.left nd left nd = nd.left nd nd.left nd left v = mi v mi p = nd.pivot p nd.pivot nd pivot nd.left = self.node(mi, p - (p&-p)//2) nd.left nd left self.node(mi, p - (p&-p)//2) self.node self node mi p - (p&-p)//2 p (p&-p)//2 p&-p p -p p 2 break nd.value = mi nd.value nd value mi if nd.right:
                        nd = nd.right
                        v = ma
                    else:
                        p = nd.pivot
                        nd.right = self.node(ma, p + (p&-p)//2)
                        break

     nd.right nd right nd = nd.right nd nd.right nd right v = ma v ma p = nd.pivot p nd.pivot nd pivot nd.right = self.node(ma, p + (p&-p)//2) nd.right nd right self.node(ma, p + (p&-p)//2) self.node self node ma p + (p&-p)//2 p (p&-p)//2 p&-p p -p p 2 break self self v v def leftmost(self, nd):
        return self.leftmost(nd.left) if nd.left else nd

     leftmost return self.leftmost(nd.left) if nd.left else nd self.leftmost(nd.left) if nd.left else nd nd.left nd left self.leftmost(nd.left) self.leftmost self leftmost nd.left nd left nd self self nd nd def rightmost(self, nd):
        return self.rightmost(nd.right) if nd.right else nd

     rightmost return self.rightmost(nd.right) if nd.right else nd self.rightmost(nd.right) if nd.right else nd nd.right nd right self.rightmost(nd.right) self.rightmost self rightmost nd.right nd right nd self self nd nd def find_l(self, v): # vより真に小さいやつの中での最大値（なければ-1）
        v += 1
        nd = self.root
        prev = nd.value if nd.value < v else 0
        while True:
            if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                prev = nd.value
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

     find_l v += 1 v 1 nd = self.root nd self.root self root prev = nd.value if nd.value < v else 0 prev nd.value if nd.value < v else 0 nd.value < v nd.value nd value v nd.value nd value 0 while True:
            if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                prev = nd.value
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

     True if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                prev = nd.value
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

     v <= nd.value v nd.value nd value if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
             nd.left nd left nd = nd.left nd nd.left nd left return prev - 1 prev - 1 prev 1 prev = nd.value prev nd.value nd value if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

     nd.right nd right nd = nd.right nd nd.right nd right return prev - 1 prev - 1 prev 1 self self v v def find_r(self, v): # vより真に大きいやつの中での最小値（なければRoot）
        v += 1
        nd = self.root
        prev = nd.value if nd.value > v else 0
        while True:
            if v < nd.value:
                prev = nd.value
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

     find_r v += 1 v 1 nd = self.root nd self.root self root prev = nd.value if nd.value > v else 0 prev nd.value if nd.value > v else 0 nd.value > v nd.value nd value v nd.value nd value 0 while True:
            if v < nd.value:
                prev = nd.value
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

     True if v < nd.value:
                prev = nd.value
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

     v < nd.value v nd.value nd value prev = nd.value prev nd.value nd value if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
             nd.left nd left nd = nd.left nd nd.left nd left return prev - 1 prev - 1 prev 1 if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

     nd.right nd right nd = nd.right nd nd.right nd right return prev - 1 prev - 1 prev 1 self self v v def max(self):
        return self.find_l((1<<self.N)-1)

     max return self.find_l((1<<self.N)-1) self.find_l((1<<self.N)-1) self.find_l self find_l (1<<self.N)-1 1<<self.N 1 self.N self N 1 self self def min(self):
        return self.find_r(-1)

     min return self.find_r(-1) self.find_r(-1) self.find_r self find_r -1 self self def delete(self, v, nd = None, prev = None): # 値がvのノードがあれば削除（なければ何もしない）
        v += 1
        if not nd:
            nd = self.root
        if not prev:
            prev = nd
        while v != nd.value:
            prev = nd
            if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return
        if (not nd.left) and (not nd.right):
            if nd.value < prev.value:
                prev.left = None
            else:
                prev.right = None
        elif not nd.left:
            if nd.value < prev.value:
                prev.left = nd.right
            else:
                prev.right = nd.right
        elif not nd.right:
            if nd.value < prev.value:
                prev.left = nd.left
            else:
                prev.right = nd.left
        else:
            nd.value = self.leftmost(nd.right).value
            self.delete(nd.value - 1, nd.right, nd)

    # v以下のものの中での最大値
     delete v += 1 v 1 if not nd:
            nd = self.root
         not nd nd nd = self.root nd self.root self root if not prev:
            prev = nd
         not prev prev prev = nd prev nd while v != nd.value:
            prev = nd
            if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return
         v != nd.value v nd.value nd value prev = nd prev nd if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return
         v <= nd.value v nd.value nd value if nd.left:
                    nd = nd.left
                else:
                    return
             nd.left nd left nd = nd.left nd nd.left nd left return if nd.right:
                    nd = nd.right
                else:
                    return
         nd.right nd right nd = nd.right nd nd.right nd right return if (not nd.left) and (not nd.right):
            if nd.value < prev.value:
                prev.left = None
            else:
                prev.right = None
        elif not nd.left:
            if nd.value < prev.value:
                prev.left = nd.right
            else:
                prev.right = nd.right
        elif not nd.right:
            if nd.value < prev.value:
                prev.left = nd.left
            else:
                prev.right = nd.left
        else:
            nd.value = self.leftmost(nd.right).value
            self.delete(nd.value - 1, nd.right, nd)

    # v以下のものの中での最大値
     (not nd.left) and (not nd.right) not nd.left nd.left nd left not nd.right nd.right nd right if nd.value < prev.value:
                prev.left = None
            else:
                prev.right = None
         nd.value < prev.value nd.value nd value prev.value prev value prev.left = None prev.left prev left None prev.right = None prev.right prev right None elif not nd.left:
            if nd.value < prev.value:
                prev.left = nd.right
            else:
                prev.right = nd.right
         not nd.left nd.left nd left if nd.value < prev.value:
                prev.left = nd.right
            else:
                prev.right = nd.right
         nd.value < prev.value nd.value nd value prev.value prev value prev.left = nd.right prev.left prev left nd.right nd right prev.right = nd.right prev.right prev right nd.right nd right elif not nd.right:
            if nd.value < prev.value:
                prev.left = nd.left
            else:
                prev.right = nd.left
         not nd.right nd.right nd right if nd.value < prev.value:
                prev.left = nd.left
            else:
                prev.right = nd.left
         nd.value < prev.value nd.value nd value prev.value prev value prev.left = nd.left prev.left prev left nd.left nd left prev.right = nd.left prev.right prev right nd.left nd left nd.value = self.leftmost(nd.right).value nd.value nd value self.leftmost(nd.right).value self.leftmost(nd.right) self.leftmost self leftmost nd.right nd right value self.delete(nd.value - 1, nd.right, nd) self.delete self delete nd.value - 1 nd.value nd value 1 nd.right nd right nd self self v v nd = None nd None prev = None prev None def upper_bound(self, v):
        upper = self.find_r(v)
        return self.find_l(upper)

     upper_bound upper = self.find_r(v) upper self.find_r(v) self.find_r self find_r v return self.find_l(upper) self.find_l(upper) self.find_l self find_l upper self self v v def lower_bound(self, v):
        lower = self.find_l(v)
        return self.find_r(lower)

     lower_bound lower = self.find_l(v) lower self.find_l(v) self.find_l self find_l v return self.find_r(lower) self.find_r(lower) self.find_r self find_r lower self self v v def __contains__(self, v: int) -> bool:
        return self.find_r(v - 1) == v

     __contains__ return self.find_r(v - 1) == v self.find_r(v - 1) == v self.find_r(v - 1) self.find_r self find_r v - 1 v 1 v self self v: int v class node:
        def __init__(self, v, p):
            self.value = v
            self.pivot = p
            self.left = None
            self.right = None node def __init__(self, v, p):
            self.value = v
            self.pivot = p
            self.left = None
            self.right = None __init__ self.value = v self.value self value v self.pivot = p self.pivot self pivot p self.left = None self.left self left None self.right = None self.right self right None self self v v p p def main():
    N = I()
    s = S()
    Q = I()
    query = LR(Q)

    trees = {ch: BalancingTree(25) for ch in alphabets}

    for i, ch in enumerate(s):
        trees[ch].append(i)

    for q in query:
        if q[0] == "1":
            _, i, c = q
            i = int(i) - 1
            prev = s[i]
            trees[prev].delete(i)
            s[i] = c
            trees[c].append(i)
        else:
            _, l, r = q
            l, r = int(l) - 1, int(r) - 1
            count = 0
            for _, tree in trees.items():
                first = tree.lower_bound(l)
                if first != tree.root.value - 1 and first <= r:
                    count += 1
            print(count) main N = I() N I() I s = S() s S() S Q = I() Q I() I query = LR(Q) query LR(Q) LR Q ch: BalancingTree(25) for ch in alphabets for ch in alphabets ch alphabets for ch in alphabets ch BalancingTree(25) BalancingTree 25 trees = {ch: BalancingTree(25) for ch in alphabets} trees {ch: BalancingTree(25) for ch in alphabets} for i, ch in enumerate(s):
        trees[ch].append(i)

     i ch enumerate(s) enumerate s trees[ch].append(i) [ch].append [ch] trees ch append i for q in query:
        if q[0] == "1":
            _, i, c = q
            i = int(i) - 1
            prev = s[i]
            trees[prev].delete(i)
            s[i] = c
            trees[c].append(i)
        else:
            _, l, r = q
            l, r = int(l) - 1, int(r) - 1
            count = 0
            for _, tree in trees.items():
                first = tree.lower_bound(l)
                if first != tree.root.value - 1 and first <= r:
                    count += 1
            print(count) q query if q[0] == "1":
            _, i, c = q
            i = int(i) - 1
            prev = s[i]
            trees[prev].delete(i)
            s[i] = c
            trees[c].append(i)
        else:
            _, l, r = q
            l, r = int(l) - 1, int(r) - 1
            count = 0
            for _, tree in trees.items():
                first = tree.lower_bound(l)
                if first != tree.root.value - 1 and first <= r:
                    count += 1
            print(count) q[0] == "1" q[0] q 0 "1" _, i, c = q _ q i c i = int(i) - 1 i int(i) - 1 int(i) int i 1 prev = s[i] prev s[i] s i trees[prev].delete(i) [prev].delete [prev] trees prev delete i s[i] = c s[i] s i c trees[c].append(i) [c].append [c] trees c append i _, l, r = q _ q l r l, r = int(l) - 1, int(r) - 1 l int(l) - 1 int(l) int l 1 r int(r) - 1 int(r) int r 1 count = 0 count 0 for _, tree in trees.items():
                first = tree.lower_bound(l)
                if first != tree.root.value - 1 and first <= r:
                    count += 1
             _ tree trees.items() trees.items trees items first = tree.lower_bound(l) first tree.lower_bound(l) tree.lower_bound tree lower_bound l if first != tree.root.value - 1 and first <= r:
                    count += 1
             first != tree.root.value - 1 and first <= r first != tree.root.value - 1 first tree.root.value - 1 tree.root.value tree.root tree root value 1 first <= r first r count += 1 count 1 print(count) print count if __name__ == '__main__':
    main() __name__ == '__main__' __name__ '__main__' main() main def LSR(n): return [LS() for _ in range(n)] def LSR(n): return [LS() for _ in range(n)] LSR def L(): return sys.stdin.readline().split() def L(): return sys.stdin.readline().split() L def LI(): return [int(x) for x in sys.stdin.readline().split()] def LI(): return [int(x) for x in sys.stdin.readline().split()] LI def IR(n): return [I() for _ in range(n)] def IR(n): return [I() for _ in range(n)] IR def LR(n): return [L() for _ in range(n)] def LR(n): return [L() for _ in range(n)] LR MOD = 1000000007 1000000007 MOD def LIR1(n): return [LI1() for _ in range(n)] def LIR1(n): return [LI1() for _ in range(n)] LIR1 def SR(n): return [S() for _ in range(n)] def SR(n): return [S() for _ in range(n)] SR def LIR(n): return [LI() for _ in range(n)] def LIR(n): return [LI() for _ in range(n)] LIR class BalancingTree:
    def __init__(self, n):
        self.N = n
        self.root = self.node(1<<n, 1<<n)

    def __str__(self):
        def debug_info(nd):
            return (nd.value - 1, nd.left.value - 1 if nd.left else None, nd.right.value - 1 if nd.right else None)

        def debug_node(nd):
            v = debug_info(nd) if nd.value else ()
            left = debug_node(nd.left) if nd.left else []
            right = debug_node(nd.right) if nd.right else []
            return [v, left, right]

        return pprint.PrettyPrinter(indent=4).pformat(debug_node(self.root))

    __repr__ = __str__

    def append(self, v):# v を追加（その時点で v はない前提）
        v += 1
        nd = self.root
        while True:
            # v がすでに存在する場合に何か処理が必要ならここに書く
            if v == nd.value:
                return 0
            else:
                mi, ma = min(v, nd.value), max(v, nd.value)
                if mi < nd.pivot:
                    nd.value = ma
                    if nd.left:
                        nd = nd.left
                        v = mi
                    else:
                        p = nd.pivot
                        nd.left = self.node(mi, p - (p&-p)//2)
                        break
                else:
                    nd.value = mi
                    if nd.right:
                        nd = nd.right
                        v = ma
                    else:
                        p = nd.pivot
                        nd.right = self.node(ma, p + (p&-p)//2)
                        break

    def leftmost(self, nd):
        return self.leftmost(nd.left) if nd.left else nd

    def rightmost(self, nd):
        return self.rightmost(nd.right) if nd.right else nd

    def find_l(self, v): # vより真に小さいやつの中での最大値（なければ-1）
        v += 1
        nd = self.root
        prev = nd.value if nd.value < v else 0
        while True:
            if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                prev = nd.value
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

    def find_r(self, v): # vより真に大きいやつの中での最小値（なければRoot）
        v += 1
        nd = self.root
        prev = nd.value if nd.value > v else 0
        while True:
            if v < nd.value:
                prev = nd.value
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

    def max(self):
        return self.find_l((1<<self.N)-1)

    def min(self):
        return self.find_r(-1)

    def delete(self, v, nd = None, prev = None): # 値がvのノードがあれば削除（なければ何もしない）
        v += 1
        if not nd:
            nd = self.root
        if not prev:
            prev = nd
        while v != nd.value:
            prev = nd
            if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return
        if (not nd.left) and (not nd.right):
            if nd.value < prev.value:
                prev.left = None
            else:
                prev.right = None
        elif not nd.left:
            if nd.value < prev.value:
                prev.left = nd.right
            else:
                prev.right = nd.right
        elif not nd.right:
            if nd.value < prev.value:
                prev.left = nd.left
            else:
                prev.right = nd.left
        else:
            nd.value = self.leftmost(nd.right).value
            self.delete(nd.value - 1, nd.right, nd)

    # v以下のものの中での最大値
    def upper_bound(self, v):
        upper = self.find_r(v)
        return self.find_l(upper)

    def lower_bound(self, v):
        lower = self.find_l(v)
        return self.find_r(lower)

    def __contains__(self, v: int) -> bool:
        return self.find_r(v - 1) == v

    class node:
        def __init__(self, v, p):
            self.value = v
            self.pivot = p
            self.left = None
            self.right = None class BalancingTree:
    def __init__(self, n):
        self.N = n
        self.root = self.node(1<<n, 1<<n)

    def __str__(self):
        def debug_info(nd):
            return (nd.value - 1, nd.left.value - 1 if nd.left else None, nd.right.value - 1 if nd.right else None)

        def debug_node(nd):
            v = debug_info(nd) if nd.value else ()
            left = debug_node(nd.left) if nd.left else []
            right = debug_node(nd.right) if nd.right else []
            return [v, left, right]

        return pprint.PrettyPrinter(indent=4).pformat(debug_node(self.root))

    __repr__ = __str__

    def append(self, v):# v を追加（その時点で v はない前提）
        v += 1
        nd = self.root
        while True:
            # v がすでに存在する場合に何か処理が必要ならここに書く
            if v == nd.value:
                return 0
            else:
                mi, ma = min(v, nd.value), max(v, nd.value)
                if mi < nd.pivot:
                    nd.value = ma
                    if nd.left:
                        nd = nd.left
                        v = mi
                    else:
                        p = nd.pivot
                        nd.left = self.node(mi, p - (p&-p)//2)
                        break
                else:
                    nd.value = mi
                    if nd.right:
                        nd = nd.right
                        v = ma
                    else:
                        p = nd.pivot
                        nd.right = self.node(ma, p + (p&-p)//2)
                        break

    def leftmost(self, nd):
        return self.leftmost(nd.left) if nd.left else nd

    def rightmost(self, nd):
        return self.rightmost(nd.right) if nd.right else nd

    def find_l(self, v): # vより真に小さいやつの中での最大値（なければ-1）
        v += 1
        nd = self.root
        prev = nd.value if nd.value < v else 0
        while True:
            if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                prev = nd.value
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

    def find_r(self, v): # vより真に大きいやつの中での最小値（なければRoot）
        v += 1
        nd = self.root
        prev = nd.value if nd.value > v else 0
        while True:
            if v < nd.value:
                prev = nd.value
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

    def max(self):
        return self.find_l((1<<self.N)-1)

    def min(self):
        return self.find_r(-1)

    def delete(self, v, nd = None, prev = None): # 値がvのノードがあれば削除（なければ何もしない）
        v += 1
        if not nd:
            nd = self.root
        if not prev:
            prev = nd
        while v != nd.value:
            prev = nd
            if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return
        if (not nd.left) and (not nd.right):
            if nd.value < prev.value:
                prev.left = None
            else:
                prev.right = None
        elif not nd.left:
            if nd.value < prev.value:
                prev.left = nd.right
            else:
                prev.right = nd.right
        elif not nd.right:
            if nd.value < prev.value:
                prev.left = nd.left
            else:
                prev.right = nd.left
        else:
            nd.value = self.leftmost(nd.right).value
            self.delete(nd.value - 1, nd.right, nd)

    # v以下のものの中での最大値
    def upper_bound(self, v):
        upper = self.find_r(v)
        return self.find_l(upper)

    def lower_bound(self, v):
        lower = self.find_l(v)
        return self.find_r(lower)

    def __contains__(self, v: int) -> bool:
        return self.find_r(v - 1) == v

    class node:
        def __init__(self, v, p):
            self.value = v
            self.pivot = p
            self.left = None
            self.right = None BalancingTree def main():
    N = I()
    s = S()
    Q = I()
    query = LR(Q)

    trees = {ch: BalancingTree(25) for ch in alphabets}

    for i, ch in enumerate(s):
        trees[ch].append(i)

    for q in query:
        if q[0] == "1":
            _, i, c = q
            i = int(i) - 1
            prev = s[i]
            trees[prev].delete(i)
            s[i] = c
            trees[c].append(i)
        else:
            _, l, r = q
            l, r = int(l) - 1, int(r) - 1
            count = 0
            for _, tree in trees.items():
                first = tree.lower_bound(l)
                if first != tree.root.value - 1 and first <= r:
                    count += 1
            print(count) def main():
    N = I()
    s = S()
    Q = I()
    query = LR(Q)

    trees = {ch: BalancingTree(25) for ch in alphabets}

    for i, ch in enumerate(s):
        trees[ch].append(i)

    for q in query:
        if q[0] == "1":
            _, i, c = q
            i = int(i) - 1
            prev = s[i]
            trees[prev].delete(i)
            s[i] = c
            trees[c].append(i)
        else:
            _, l, r = q
            l, r = int(l) - 1, int(r) - 1
            count = 0
            for _, tree in trees.items():
                first = tree.lower_bound(l)
                if first != tree.root.value - 1 and first <= r:
                    count += 1
            print(count) main alphabets = "abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz" alphabets dire = [[1, 0], [0, 1], [-1, 0], [0, -1]] [[1, 0], [0, 1], [-1, 0], [0, -1]] dire def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()] def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()] LI1 def S(): return list(sys.stdin.readline().rstrip()) def S(): return list(sys.stdin.readline().rstrip()) S def LS(): return [list(x) for x in sys.stdin.readline().split()] def LS(): return [list(x) for x in sys.stdin.readline().split()] LS dire8 = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]] [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]] dire8 def I(): return int(sys.stdin.readline().rstrip()) def I(): return int(sys.stdin.readline().rstrip()) I
#!usr/bin/env python3
from collections import defaultdict, deque, Counter, OrderedDict
from bisect import bisect_left, bisect_right
from functools import reduce, lru_cache
from heapq import heappush, heappop, heapify

import itertools, bisect
import math, fractions
import sys, copy

def L(): return sys.stdin.readline().split()
def I(): return int(sys.stdin.readline().rstrip())
def S(): return list(sys.stdin.readline().rstrip())
def LI(): return [int(x) for x in sys.stdin.readline().split()]
def LI1(): return [int(x) - 1 for x in sys.stdin.readline().split()]
def LS(): return [list(x) for x in sys.stdin.readline().split()]
def IR(n): return [I() for _ in range(n)]
def LIR(n): return [LI() for _ in range(n)]
def LIR1(n): return [LI1() for _ in range(n)]
def SR(n): return [S() for _ in range(n)]
def LSR(n): return [LS() for _ in range(n)]
def LR(n): return [L() for _ in range(n)]

alphabets = "abcdefghijklmnopqrstuvwxyz"

sys.setrecursionlimit(1000000)
dire = [[1, 0], [0, 1], [-1, 0], [0, -1]]
dire8 = [[1, 0], [1, 1], [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1]]
MOD = 1000000007

# nodeをリストに変換したらクソ遅かった

import pprint

class BalancingTree:
    def __init__(self, n):
        self.N = n
        self.root = self.node(1<<n, 1<<n)

    def __str__(self):
        def debug_info(nd):
            return (nd.value - 1, nd.left.value - 1 if nd.left else None, nd.right.value - 1 if nd.right else None)

        def debug_node(nd):
            v = debug_info(nd) if nd.value else ()
            left = debug_node(nd.left) if nd.left else []
            right = debug_node(nd.right) if nd.right else []
            return [v, left, right]

        return pprint.PrettyPrinter(indent=4).pformat(debug_node(self.root))

    __repr__ = __str__

    def append(self, v):# v を追加（その時点で v はない前提）
        v += 1
        nd = self.root
        while True:
            # v がすでに存在する場合に何か処理が必要ならここに書く
            if v == nd.value:
                return 0
            else:
                mi, ma = min(v, nd.value), max(v, nd.value)
                if mi < nd.pivot:
                    nd.value = ma
                    if nd.left:
                        nd = nd.left
                        v = mi
                    else:
                        p = nd.pivot
                        nd.left = self.node(mi, p - (p&-p)//2)
                        break
                else:
                    nd.value = mi
                    if nd.right:
                        nd = nd.right
                        v = ma
                    else:
                        p = nd.pivot
                        nd.right = self.node(ma, p + (p&-p)//2)
                        break

    def leftmost(self, nd):
        return self.leftmost(nd.left) if nd.left else nd

    def rightmost(self, nd):
        return self.rightmost(nd.right) if nd.right else nd

    def find_l(self, v): # vより真に小さいやつの中での最大値（なければ-1）
        v += 1
        nd = self.root
        prev = nd.value if nd.value < v else 0
        while True:
            if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                prev = nd.value
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

    def find_r(self, v): # vより真に大きいやつの中での最小値（なければRoot）
        v += 1
        nd = self.root
        prev = nd.value if nd.value > v else 0
        while True:
            if v < nd.value:
                prev = nd.value
                if nd.left:
                    nd = nd.left
                else:
                    return prev - 1
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return prev - 1

    def max(self):
        return self.find_l((1<<self.N)-1)

    def min(self):
        return self.find_r(-1)

    def delete(self, v, nd = None, prev = None): # 値がvのノードがあれば削除（なければ何もしない）
        v += 1
        if not nd:
            nd = self.root
        if not prev:
            prev = nd
        while v != nd.value:
            prev = nd
            if v <= nd.value:
                if nd.left:
                    nd = nd.left
                else:
                    return
            else:
                if nd.right:
                    nd = nd.right
                else:
                    return
        if (not nd.left) and (not nd.right):
            if nd.value < prev.value:
                prev.left = None
            else:
                prev.right = None
        elif not nd.left:
            if nd.value < prev.value:
                prev.left = nd.right
            else:
                prev.right = nd.right
        elif not nd.right:
            if nd.value < prev.value:
                prev.left = nd.left
            else:
                prev.right = nd.left
        else:
            nd.value = self.leftmost(nd.right).value
            self.delete(nd.value - 1, nd.right, nd)

    # v以下のものの中での最大値
    def upper_bound(self, v):
        upper = self.find_r(v)
        return self.find_l(upper)

    def lower_bound(self, v):
        lower = self.find_l(v)
        return self.find_r(lower)

    def __contains__(self, v: int) -> bool:
        return self.find_r(v - 1) == v

    class node:
        def __init__(self, v, p):
            self.value = v
            self.pivot = p
            self.left = None
            self.right = None

def main():
    N = I()
    s = S()
    Q = I()
    query = LR(Q)

    trees = {ch: BalancingTree(25) for ch in alphabets}

    for i, ch in enumerate(s):
        trees[ch].append(i)

    for q in query:
        if q[0] == "1":
            _, i, c = q
            i = int(i) - 1
            prev = s[i]
            trees[prev].delete(i)
            s[i] = c
            trees[c].append(i)
        else:
            _, l, r = q
            l, r = int(l) - 1, int(r) - 1
            count = 0
            for _, tree in trees.items():
                first = tree.lower_bound(l)
                if first != tree.root.value - 1 and first <= r:
                    count += 1
            print(count)



if __name__ == '__main__':
    main()

