#include <bits/stdc++.h>
  
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/detail/standard_policies.hpp>
using namespace __gnu_pbds;
 
using namespace std;
 
#if !ONLINE_JUDGE
#define debug
#endif
 
typedef long long ll;
typedef vector<int> vi;
typedef pair<int,int> ii;
typedef vector<pair<int,int> > vii;
typedef pair<ll,ll> pll;
typedef vector<vector<int> > vvi;
typedef vector<vector<ii> > vvii;
 
#define pb push_back
#define mp make_pair
#define all(x) x.begin(),x.end()
#define sz(x) (int)x.size()
#define fill(a,x) memset(a,x,sizeof(a))
#define ff first
#define ss second
#define trav(a, x) for(auto& a : x) 
#define FOR(i,a,b) for(int i = a; i<=b; ++i)
#define NFOR(i,a,b) for(int i = a; i>=b; --i)

#ifdef debug
#define dbg(...) { cerr<<"[ "; dump(#__VA_ARGS__, __VA_ARGS__);}
#undef endl
template <typename Arg1,typename Arg2>
ostream& operator << (ostream& out, const pair<Arg1,Arg2> &x) {
    return out<<"("<<x.ff<<","<<x.ss<<")";
}
 
template <typename Arg1>
ostream& operator << (ostream& out, const vector<Arg1> &a) {
    out<<"[";for(const auto &x:a)out<<x<<",";return out<<"]";
}
 
template <typename Arg1>
ostream& operator << (ostream& out, const set<Arg1> &a) {
    out<<"[";for(const auto &x:a)out<<x<<",";return out<<"]";
}
 
template <typename Arg1,typename Arg2>
ostream& operator << (ostream& out, const map<Arg1,Arg2> &a) {
    out<<"[";for(const auto &x:a)out<<x<<",";return out<<"]";
}
 
template <typename Arg1,typename Arg2>
ostream& operator << (ostream& out, const unordered_map<Arg1,Arg2> &a) {
    out<<"[";for(const auto &x:a)out<<x<<",";return out<<"]";
} 
 
template <typename Arg1>
void dump(const string name, Arg1&& arg1){
    cerr << name << " : " << arg1 << " ] " << endl;
}
 
template <typename Arg1, typename... Args>
void dump(const string names, Arg1&& arg1, Args&&... args){
    const string name = names.substr(0,names.find(','));
    cerr<<name<<" : "<<arg1<<" | ";
    dump(names.substr(1+(int)name.size()), args...);
}
#else
    #define dbg(args...)
#endif
 
ll gcd(ll a,ll b){if (a==0) return b;return gcd(b%a,a);}
ll lcm(ll a,ll b){return a*(b/gcd(a,b));}
ll Abs(ll a){if(a>0)return a;return -a;}
ll Ceil(ll a,ll b){if(a%b==0)return a/b;else return a/b+1;}
double Abs(double a){if(a>0)return a;return -a;}
 
//*X.find_by_order(2) element at index=2
//X.order_of_key(1) how many elements strictly less than 1
 
#define ordered_set tree<int, null_type,less_equal<int>, rb_tree_tag,tree_order_statistics_node_update> 

const ll INF = 1e18;
const int mod = 1e9 + 7;
 
inline int pow_(ll x, ll y,ll p){
  int r = 1; while(y){if(y & 1) r = r * x % p; y >>= 1; x = x * x % p;} return r;
}
inline int inv_(int x){return pow_(x, mod - 2, mod);}

// Follwing functions work only when both a and b are non-negative
inline int add(ll a,ll b){a += b; if(a >= mod) a -= mod; return a;}
inline int mul(int a, int b){return a * 1LL * b % mod;}
inline int sub(int a, int b){a -= b; if(a < 0) a += mod; return a;}
  
mt19937_64 mrand(chrono::steady_clock::now().time_since_epoch().count());
 
const int N = 5e5+5;

int n;
int t[4*N][26];
int a[N];

vector<int> merge(vector<int> v1,vector<int> v2){
    vector<int> v;
    int i = 0;
    int j = 0;
    while(i < v1.size() && j < v2.size()){
        if(v1[i] < v2[j]){
            v.pb(v1[i]);
            i++;
        } else {
            v.pb(v2[j]);
            j++;
        }
    }

    while(i < v1.size()){
        v.pb(v1[i]);
        i++;
    }

    while(j < v2.size()){
        v.pb(v2[j]);
        j++;
    }

    vector<int> t;
    int k = v.size();
    for(int i=0;i<k;i++){
        t.pb(v[i]);
        int j = i;
        while(v[j] == v[i] && j < k){
            j++;
        }
        i = j - 1;
    }

    return t;
}

void build(int node,int tl,int tr){

    if(tl > tr){
        return;
    }

    if(tl==tr){
        int k = a[tl];
        t[node][k] = 1;
        return;
    }

    int tm = (tl+tr)/2;
    build(2*node,tl,tm);
    build(2*node+1,tm+1,tr);

    for(int i=0;i<26;i++){
        t[node][i] = t[2*node][i] + t[2*node+1][i];
    }
}

vector<int> query(int node,int tl,int tr,int l,int r){
    vector<int> v;
    if(l>r)return v; // change to a value which does not effect result
    if(l==tl && r==tr){
        int c = 0;
        for(int i=0;i<26;i++){
            if(t[node][i])v.pb(i);
        }
        return v;
    }
    int tm = (tl+tr)/2;
    return merge(query(2*node,tl,tm,l,min(tm,r)),query(2*node+1,tm+1,tr,max(tm+1,l),r));
}

void update(int node,int tl,int tr,int pos,int new_val){
    if(tl == tr){
        t[node][a[pos]]--;
        t[node][new_val]++;
        a[pos] = new_val;
        return;
    }

    int tm = (tl+tr)/2;
    if(pos<=tm){
        update(2*node,tl,tm,pos,new_val);
    } else {
        update(2*node+1,tm+1,tr,pos,new_val);   
    }

    for(int i=0;i<26;i++){
        t[node][i] = t[2*node][i] + t[2*node+1][i];
    }
}


int main(int argc, char const *argv[])
{
    #ifdef LOCAL_TEST
    freopen("in.txt","r",stdin);
    //freopen("out.txt", "w", stdout);
    #else
    ios_base::sync_with_stdio(false);cin.tie(NULL);
    #endif

    auto start = std::chrono::high_resolution_clock::now();

    cin>>n;
    string s;
    cin>>s;
    for(int i=0;i<n;i++){
        a[i+1] = s[i] - 'a';
    }

    build(1,1,n);

    int q;
    cin>>q;
    FOR(i,1,q){
        int t;
        cin>>t;
        if(t == 1){
            int i;
            char q;
            cin>>i>>q;
            int k = q - 'a';
            update(1,1,n,i,k);
        } else {
            int l,r;
            cin>>l>>r;
            vector<int> v = query(1,1,n,l,r);
            cout << v.size() << "\n";
        }
    }

    auto finish = std::chrono::high_resolution_clock::now();  
    std::chrono::duration<double> elapsed = finish - start;
    cerr << "Elapsed time: " << elapsed.count() << " s\n";
    
    return 0;    
}

Measured memory_bytes: 109596