// 問題の URL を書いておく
// 

#include <algorithm>
#include <array>
#include <bitset>
#include <cmath>
#include <iostream>
#include <list>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <vector>

using namespace std;

//#define ENABLE_PRINT

#if defined(ENABLE_PRINT)

#define Print(v) \
do {\
    cout << #v << ": " << v << endl; \
}while(0)

#define PrintVec(v) \
do {\
    for(int __i = 0; __i < v.size(); ++__i) \
    { \
        cout << #v << "[" << __i << "]: " << v[__i] << endl; \
    }\
}while(0)

#else

#define Print(v) ((void)0)
#define PrintVec(v) ((void)0)

#endif

#define rep(i, n) for(int i = 0; i < (int)(n); ++i)

using ll = int64_t;

const ll NMax = 512 * 1024;

vector<char> nodes[NMax * 2 - 1];

template<typename T>
class SegmentTreeBase
{
public:
    SegmentTreeBase(int n)
    {
        m_N = n;
        m_BottomSize = 1;
        m_Depth = 0;
        while(m_BottomSize < m_N)
        {
            m_Depth++;
            m_BottomSize *= 2;
        }
        auto storageSize = m_BottomSize * 2 - 1;
        m_Storage = vector<T>(storageSize);
        m_BottomIndexBase = m_BottomSize - 1;
    }

    void Set(int index, const T& v)
    {
        if(!(index >= 0 && index < m_N))
        {
            printf("invalide index: %d\n", index);
            abort();
        }
        m_Storage[m_BottomIndexBase + index] = v;
    }

    void Build()
    {
        auto bi = m_BottomIndexBase / 2;
        rep(i, m_Depth)
        {
            rep(j, bi + 1)
            {
                Merge(m_Storage[bi + j], m_Storage[(bi + j) * 2 + 1], m_Storage[(bi + j) * 2 + 2]);
            }
            bi /= 2;
        }
    }

    void Query(T& out, int a, int b)
    {
//        Dump();
        QueryImpl(out, a, b, 0, m_BottomSize, 0);
    }

    virtual void Merge(T& out, const T& a, const T& b) = 0;

    void Update(int a, const T& v)
    {
        auto bi = m_BottomIndexBase + a;
        m_Storage[bi] = v;
        while(bi > 0)
        {
            bi = (bi - 1) / 2;
            Merge(m_Storage[bi], m_Storage[bi * 2 + 1], m_Storage[bi * 2 + 2]);
        }
    }

    virtual string ToString(const T& t) = 0;

    void Dump()
    {
        rep(i, m_Storage.size())
        {
            auto& t = m_Storage[i];
            printf("%d: %s\n", i, ToString(t).c_str());
        }
    }

private:
    void QueryImpl(T& out, int a, int b, int l, int r, int k)
    {
        if(b <= l || a >= r)
        {
            out = T();
            return;
        }

        if(l >= a && r <= b)
        {
            out = m_Storage[k];
            return;
        }

        T lc, rc;
        QueryImpl(lc, a, b, l, (l + r) / 2, k * 2 + 1);
        QueryImpl(rc, a, b, (l + r) / 2, r, k * 2 + 2);
        Merge(out, lc, rc);
    }
    vector<T> m_Storage;
    int m_Depth;
    int m_BottomSize;
    int m_BottomIndexBase;
    int m_N;
};

void merge(vector<char>& out, const vector<char>& a, const vector<char>& b)
{
    out = a;
    for(auto c : b)
    {
        if(find(out.begin(), out.end(), c) == out.end())
        {
            out.push_back(c);
        }
    }
}

void query(vector<char>& out, int a, int b, int l, int r, int k)
{
    if(k < 0) abort();
//    printf("a=%d, b=%d, l=%d, r=%d, k=%d\n", a, b, l, r, k);
    if(a >= r || b <= l)
    {
        out.clear();
        return;
    }
    if(a <= l && r <= b)
    {
        out = nodes[k];
        return;
    }
    vector<char> lc, rc;
    query(lc, a, b, l, (l + r) / 2, k * 2 + 1);
    query(rc, a, b, (l + r) / 2, r, k * 2 + 2);
    merge(out, lc, rc);
}

class SegmentTree : public SegmentTreeBase<vector<char>>
{
public:
    SegmentTree(int n) : SegmentTreeBase(n) {}
    virtual void Merge(vector<char>& out, const vector<char>& a, const vector<char>& b)
    {
        out = a;
        for(auto x : b)
        {
            if(find(out.begin(), out.end(), x) == out.end())
            {
                out.push_back(x);
            }
        }
    }
    virtual string ToString(const vector<char>& v) 
    {
        string s;
        for(auto& c: v)
        {
            s += c;
        }
        return s;
    }

};

int main(int, const char**)
{
    int n;
    cin >> n;
    string s;
    cin >> s;

    SegmentTree st(n);
    rep(i, n)
    {
        vector<char> c;
        c.push_back(s[i]);
        st.Set(i, c);
    }
    st.Build();

//    st.Dump();

    int q;
    cin >> q;
    rep(i, q)
    {
        int t;
        cin >> t;
        if(t == 1)
        {
            int a; char b;
            cin >> a >> b;
            a--;
            vector<char> c;
            c.push_back(b);
            st.Update(a, c);
        }
        else
        {
            int a, b;
            cin >> a >> b;
            a--; 
            vector<char> c;
            st.Query(c, a, b);
            cout << c.size() << endl;
        }
    }

#if 0
    // start depth
    int sd = (int)ceil(log2(n));
    // start index
    auto si = (int)pow(2, sd) - 1;

    rep(i, n)
    {
        nodes[si + i].push_back(s[i]); 
    }
    rep(i, sd)
    {
        auto index = (int)pow(2, sd - 1 - i) - 1;
        rep(j, index + 1)
        {
            merge(nodes[index + j], nodes[(index + j) * 2 + 1], nodes[(index + j) * 2 + 2]);
        }
    }

    int q;
    cin >> q;
    rep(i, q)
    {
        int t;
        cin >> t;
        if(t == 1)
        {
            int a; char b;
            cin >> a >> b;
            a--;
            auto bi = si + a;
            nodes[bi].clear();
            nodes[bi].push_back(b);
            while(bi != 0)
            {
                bi = (bi - 1) / 2;
                merge(nodes[bi], nodes[bi * 2 + 1], nodes[bi * 2 + 2]);
            }
        }
        else
        {
            int a, b;
            cin >> a >> b;
            a--; 
            vector<char> c;
            query(c, a, b, 0, (int)pow(2, sd), 0);
            cout << c.size() << endl;
        }
    }
#endif
    return 0;
}


Measured memory_bytes: 84540