#include <bits/stdc++.h>

using namespace std;
typedef long long int ll;
typedef pair <ll,ll> P;
#define REP(i, n) for(ll i = 0LL; i < (ll)(n); i++)
#define REPR(i, n) for(ll i = n-1; i >= 0LL; i--)
#define FOR(i, n, m) for(ll i = n; i < (ll)(m); i++)
#define PRINT(x) cout << x << endl
#define ALL(v) v.begin(), v.end()
#define INF (ll)1e18
#define INFD 1e18
#define PI 3.14159265358979
#define MOD 1000000007
// #define MOD 998244353
#define VL vector<ll>

vector<ll> dx = {-1, 1, 0, 0};
vector<ll> dy = {0, 0, 1, -1};

ll gcd(ll a, ll b) { return b ? gcd(b,a%b) : a;}
ll lcm(ll a, ll b) { return a / gcd(a, b) * b; }



/*
実装では木を1-indexedの配列で表現している。
ノードkについて、親ノードはk/2、子ノードは2k, 2k+1である。

・SegmentTree(n, f, M1) := サイズnの初期化。
ここでfは2つの区間の要素をマージする二項演算、M1はモノイドの単位元
・set(k, x) := k番目の要素にxを代入する。
・build() := セグメント木を構築する。
・query(a, b) := 区間[a,b)に対して二項演算した結果を返す。
・update(k, x) := k番目の要素をxに変更する。
・operator[k] := k番目の要素を返す。
・find_first(a, check) := [a,x)がcheckを満たす最初の要素位置xを返す。
・find_last(b, check) := (x, b]がcheckを満たす最後の要素位置xを返す。
*/
template< typename Monoid >
struct SegmentTree {
	using F = function< Monoid(Monoid, Monoid) >;

	int sz;
	vector< Monoid > seg;

	const F f;
	const Monoid M1;

	SegmentTree(int n, const F f, const Monoid &M1) : f(f), M1(M1) {
		sz = 1;
		while(sz < n) sz <<= 1;
		seg.assign(2 * sz, M1);
	}

	void set(int k, const Monoid &x) {
		seg[k + sz] = x;
	}

	void build() {
		for(int k = sz - 1; k > 0; k--) {
			seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
		}
	}

	void update(int k, const Monoid &x) {
		k += sz;
		seg[k] = x;
		while(k >>= 1) {
			seg[k] = f(seg[2 * k + 0], seg[2 * k + 1]);
		}
	}

	Monoid query(int a, int b) {
		Monoid L = M1, R = M1;
		for(a += sz, b += sz; a < b; a >>= 1, b >>= 1) {
			if(a & 1) L = f(L, seg[a++]);
			if(b & 1) R = f(seg[--b], R);
		}
		return f(L, R);
	}

	Monoid operator[](const int &k) const {
		return seg[k + sz];
	}

	template< typename C >
	int find_subtree(int a, const C &check, Monoid &M, bool type) {
		while(a < sz) {
			Monoid nxt = type ? f(seg[2 * a + type], M) : f(M, seg[2 * a + type]);
			if(check(nxt)) a = 2 * a + type;
			else M = nxt, a = 2 * a + 1 - type;
		}
		return a - sz;
	}


	template< typename C >
	int find_first(int a, const C &check) {
		Monoid L = M1;
		if(a <= 0) {
			if(check(f(L, seg[1]))) return find_subtree(1, check, L, false);
			return -1;
		}
		int b = sz;
		for(a += sz, b += sz; a < b; a >>= 1, b >>= 1) {
			if(a & 1) {
				Monoid nxt = f(L, seg[a]);
				if(check(nxt)) return find_subtree(a, check, L, false);
				L = nxt;
				++a;
			}
		}
		return -1;
	}

	template< typename C >
	int find_last(int b, const C &check) {
		Monoid R = M1;
		if(b >= sz) {
			if(check(f(seg[1], R))) return find_subtree(1, check, R, true);
			return -1;
		}
		int a = sz;
		for(b += sz; a < b; a >>= 1, b >>= 1) {
			if(b & 1) {
				Monoid nxt = f(seg[--b], R);
				if(check(nxt)) return find_subtree(b, check, R, true);
				R = nxt;
			}
		}
		return -1;
	}
};



int main()
{
	ll N, Q;
	string S;
	cin >> N >> S >> Q;
	vector<set<char>> v(N);
	REP(i, N) {
		v[i].insert(S[i]);
	}
  SegmentTree<set<char>> seg(N, [](set<char> a,set<char> b){for(char l : b) a.insert(l); return a;}, set<char>());
	REP(i, N) {
		seg.set(i, v[i]);
	}
	seg.build();
	REP(i, Q) {
		ll q;
		cin >> q;
		if (q == 1) {
			ll i;
			char c;
			cin >> i >> c;
			i--;
			set<char> t;
			t.insert(c);
			seg.update(i, t);
		} else {
			ll l, r;
			cin >> l >> r;
			l--; r;
			auto ss = seg.query(l, r);
			PRINT(ss.size());
		}
	}
	return 0;
}


Measured memory_bytes: 235652