#if 1
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <queue>
#include <stack>
#include <array>
#include <deque>
#include <algorithm>
#include <utility>
#include <cstdint>
#include <functional>
#include <iomanip>
#include <numeric>
#include <assert.h>
#include <bitset>
#include <list>
#include <cmath>

auto& in = std::cin;
auto& out = std::cout;
#define all_range(C) std::begin(C), std::end(C)
const double PI = 3.141592653589793238462643383279502884197169399375105820974944;

inline constexpr uint32_t get_min2pow(uint32_t value, uint32_t work = 1) {
	return ((value <= work) ? (work) : (get_min2pow(value, work * 2)));
}

class segment_tree
{
public:
	using value_type = int32_t;
	static constexpr uint32_t N_MAX = get_min2pow(500000);
private:
	value_type default_value;
	value_type value[N_MAX * 2 - 1] = {};
	static inline constexpr int32_t getchild(int i) {
		return i * 2 + 1;
	}
	static inline constexpr int32_t getparent(int i) {
		return (i - 1) / 2;
	}
	void Init_impl(int k = 0)
	{
		if (k < N_MAX - 1) {
			//ノード
			auto c1 = getchild(k), c2 = c1 + 1;
			Init_impl(c1);
			Init_impl(c2);
			value[k] = merge(value[c1], value[c2]);
		}
		else {
			//葉
			value[k] = default_value;
		}
	}
	value_type query_impl(int32_t a, int32_t b, int32_t k = 0, int32_t l = 0, int32_t r = N_MAX)const
	{
		if (r <= a || b <= l)return default_value;
		if (a <= l && r <= b)return value[k];
		int m = (l + r) / 2;
		return merge(query_impl(a, b, getchild(k), l, m), query_impl(a, b, getchild(k) + 1, m, r));
	}

public:
	template<typename T>
	auto change(int32_t i, T&& v)
	{
		return change(i, v, std::plus<>{});
	}
	template<typename T, typename FUNC>
	void change(int32_t i, T&& v, FUNC&& operation)
	{
		i += N_MAX - 1;
		//葉
		value[i] = std::forward<FUNC>(operation)(std::move(value[i]), std::forward<T>(v));
		i = getparent(i);
		//ノード
		for (;;) {
			auto c1 = getchild(i), c2 = c1 + 1;
			value[i] = merge(value[c1], value[c2]);
			if (i == 0) { break; }
			i = getparent(i);
		}
	}

	//-----------------------------変更するコードここから-----------------------------//
private:
	static inline value_type merge(const value_type& l, const value_type& r)
	{
		//RSQ
		return l + r;
	}
public:
	inline segment_tree()
	{
		init();
	}
	inline void init()
	{
		default_value = 0;
		Init_impl();
	}
	inline value_type query(int32_t a, int32_t b)const
	{
		return query_impl(a, b);
	}
};

int32_t N;
std::string S;
segment_tree num[30];
int main()
{
    using std::endl;
    in.sync_with_stdio(false);
    out.sync_with_stdio(false);
    in.tie(nullptr);
    out.tie(nullptr);

    in >> N;
	in >> S;
	for (size_t i = 0; i < N; i++)
	{
		num[S[i] - 'a'].change(i, 1);
	}
	int Q;
	in >> Q;
	while (Q--)
	{
		int type;
		in >> type;
		if (type == 1)
		{
			int i;
			in >> i; --i;
			char str[10];
			in >> str;
			num[S[i] - 'a'].change(i, -1);
			S[i] = str[0];
			num[S[i] - 'a'].change(i, +1);
		}
		else
		{
			int l, r;
			in >> l >> r; --l; --r;
			++r;

			int count = 0;
			for (auto& tree : num) {
				if (0 < tree.query(l, r)) {
					++count;
				}
			}
			out << count << endl;
		}
	}


    return 0;
}
#endif


Measured memory_bytes: 124028