// コメント, ロジックを修正して再提出.
#include <bits/stdc++.h>
using namespace std;
#define repex(i, a, b, c) for(int i = a; i < b; i += c)
#define repx(i, a, b) repex(i, a, b, 1)
#define rep(i, n) repx(i, 0, n)
#define repr(i, a, b) for(int i = a; i >= b; i--)
#define pb push_back

// ※※※ LazySegmentTree は, 以下のソースをベースとしている(一部変更) ※※※
// zscoder氏.
// https://atcoder.jp/contests/dp/submissions/3941934
class LazySegmentTree{
private:
    int size_;
    vector<int> v, lazy;
    void update(int a, int b, int x, int k, int l, int r){
        push(k, l, r);
        if(r <= a || b <= l) return;
        if(a <= l && r <= b){
            lazy[k] = x;
            push(k, l, r);
        }else{
            update(a, b, x, k * 2,                l, (l + r) >> 1);
            update(a, b, x, k * 2 + 1, (l + r) >> 1,            r);
            v[k] = merge(v[k * 2], v[k * 2 + 1]);
        }
    }
    int query(int a, int b, int k, int l, int r){
        push(k, l, r);
        if(r <= a || b <= l) return 0;
        if(a <= l && r <= b) return v[k];
        int lc = query(a, b, k * 2,                l, (l + r) >> 1);
        int rc = query(a, b, k * 2 + 1, (l + r) >> 1,            r);
        return merge(lc, rc);
    }

public:
    LazySegmentTree() : v(vector<int>()), lazy(vector<int>()) {};
    LazySegmentTree(int n){
        for(size_ = 1; size_ < n;) size_ <<= 1;
        v.resize(size_ * 2, 0);
        lazy.resize(size_ * 2, 0);
    }
    inline void push(int k, int l, int r){
        if(lazy[k] != 0){
            v[k] += lazy[k];
            if(r - l > 1){
                lazy[k * 2]     += lazy[k];
                lazy[k * 2 + 1] += lazy[k];
            }
            lazy[k] = 0;
        }
    }
    inline int merge(int x, int y){
        return max(x, y);
        // return x + y;
    }
    inline void update(int l, int r, int x){
        update(l, r, x, 1, 0, size_);
    }
    inline int query(int l, int r){
        return query(l, r, 1, 0, size_);
    }
};

int main(){
    
    // 1. 入力情報.
    int N, Q;
    char c[505050];
    scanf("%d %s %d", &N, c, &Q);
    
    // 2. 区間更新(遅延伝播セグメント木).
    LazySegmentTree seg(505050);
    vector<LazySegmentTree> vSeg;
    rep(i, 26) vSeg.pb(seg);
    rep(j, N) vSeg[c[j] - 'a'].update(j, j + 1, 1);
    // rep(i, 26){
    //     rep(j, N) printf("%d ", vSeg[i].query(j, j + 1));
    //     puts("");
    // }
    
    // 3. 各クエリに回答.
    int n, iq, lq, rq, bef, cur, ans, bCnt, cCnt;
    char cq[5];
    rep(i, Q){
        scanf("%d", &n);
        if(n == 1){
            scanf("%d %s", &iq, cq);
            iq--;
            
            // 変更前の文字.
            bef = c[iq] - 'a';
            
            // 変更後の文字.
            cur = cq[0] - 'a';
            
            // 変更前文字の出現個数.
            bCnt = vSeg[bef].query(iq, iq + 1);
            
            // 変更前文字を減算.
            if(bCnt > 0) vSeg[bef].update(iq, iq + 1, -1);
            
            // 変更後文字の出現個数.
            cCnt = vSeg[cur].query(iq, iq + 1);
            
            // 変更後文字を加算.
            if(cCnt == 0) vSeg[cur].update(iq, iq + 1, 1);
            
            // 文字列を, 変更後文字 に 更新.
            c[iq] = cq[0];
        }else{
            scanf("%d %d", &lq, &rq);
            lq--, ans = 0;
            
            // 部分文字列(区間[lq, rq]) の 種類数は？
            rep(j, 26) ans += vSeg[j].query(lq, rq);
            
            // 出力.
            printf("%d\n", ans);
        }
    }
    return 0;
    
}

Measured memory_bytes: 224128