#ifdef ONLINE_JUDGE
#include <bits/stdc++.h>
#else
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <set>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#include <cstdlib>
#endif

using namespace std;

	// lambda : [] (int a, int b) -> bool { body return }
	// string r_str = R"(raw string)"

#define mp make_pair
#define eb emplace_back
#define pb push_back
#define LL long long
#define ULL unsigned long long
#define BASE 73
#define NMAX 10000
#define NMAX2 20001
#define MOD1 1000000007
#define ALL(V) (V).begin(), (V).end()
#define ALLR(V) (V).rbegin(), (V).rend()
#define CRLINE Duxar(__LINE__);
#define SHOWME(x) cerr << __LINE__ << ": " << #x << " = " << (x) << endl;
#define ENTER putchar('\n');

int dx4[] = {-1, 0, 1, 0};
int dy4[] = {0, 1, 0, -1};

int dx6[] = {-1, -1, 0, 1, 1, 1, 0, -1};
int dy6[] = {0, 1, 1, 1, 0, -1, -1, -1};

void Duxar(int _this_line) {
#ifndef ONLINE_JUDGE
	printf("\n . . . . . . . . . . . . . Passed line - %d\n", _this_line);
#endif
}

template <class T>
void ReadNo(T &_value) {
	T _sign = 1;
	char ch;
	_value = 0;
	while(!isdigit(ch = getchar())) {
		(ch == '-') && (_sign = -1);
	}
	do {
		_value = _value * 10 + (ch - '0');
	} while(isdigit(ch = getchar()));
	_value *= _sign;
}

template <class T>
void AddNr(T &a, T b) {
	a = a + b;
	while (a >= MOD1) {
		a -= MOD1;
	}
	while (a < 0) {
		a += MOD1;
	}
}

int N, M, pos_node = 1;
vector <int> values, partial_values, p2, first_found, euler_dfs, level;
vector <pair <int, int> > queries;
vector <vector <int> > rmq, graph;

void DFSValues(int iam, int from);
void DFSEuler(int iam, int from, int node_level);
void PrepareRMQ();

int main(){
#ifndef ONLINE_JUDGE

//		freopen("", "r", stdin);
#endif
	
	int i, x, y, t, px, py, k, dif, ans;
	char c;
	
	scanf("%d %d\n", &N, &M);
	
	values.resize(N + 1);
	graph.resize(N + 1);
	for (i = 1; i <= N; ++i) {
		scanf("%c", &c);
		values[i] = 1 << (c - 'a');
	}
	scanf("\n");
	for (i = 1; i < N; ++i) {
		ReadNo(x); ReadNo(y);
		graph[x].pb(y);
		graph[y].pb(x);
	}
	
	for (i = 1; i <= M; ++i) {
		scanf("%d", &t);
		if (t == 1) {
			scanf("%d %d\n", &x, &y);
			queries.pb({x, y});
		}
		else {
			scanf("%d %c\n", &x, &c);
			++N;
			values.pb(1 << (c - 'a'));
			graph.pb(vector<int> ());
			graph[N].pb(x);
			graph[x].pb(N);
		}
	}
	
	
	first_found.resize(N + 1);
	level.resize(N * 10);
	euler_dfs.resize(N * 10);
	
	partial_values.resize(N + 1);
	DFSValues(1, -1);
	DFSEuler(1, -1, 0);
	PrepareRMQ();
	
	for (auto qr: queries) {
		x = qr.first;
		y = qr.second;
		px = first_found[x];
		py = first_found[y];
		if (py < px) {
			swap (py, px);
		}
		dif = py - px + 1;
		k = p2[dif];
		ans = rmq[k][px];
		if (level[ans] > level[rmq[k][py - (1 << k) + 1]]) {
			ans = rmq[k][py - (1 << k) + 1];
		}
		ans = euler_dfs[ans];
		ans = partial_values[x] ^ partial_values[y] ^ values[ans];
		int nr = 0;
		while (ans) {
			nr += ans & 1;
			ans >>= 1;
		}
		printf("%d\n", nr);
	}
	
	return 0;
}

void DFSValues(int iam, int from) {
	partial_values[iam] ^= values[iam];
	for (auto to: graph[iam]) {
		if (to != from) {
			partial_values[to] = partial_values[iam];
			DFSValues(to, iam);
		}
	}
}

void DFSEuler(int iam, int from, int node_level) {
	first_found[iam] = pos_node;
	level[pos_node] = node_level;
	euler_dfs[pos_node] = iam;
	++pos_node;
	
	for (auto to: graph[iam]) {
		if (to != from) {
			DFSEuler(to, iam, node_level + 1);
			level[pos_node] = node_level;
			euler_dfs[pos_node] = iam;
			++pos_node;
		}
	}
}

void PrepareRMQ() {
	int i, k;
	--pos_node;
	rmq.resize(20, vector<int> (pos_node + 1));
	p2.resize(pos_node + 1);
	rmq[0][1] = 1;
	for (i = 2; i <= pos_node; ++i) {
		rmq[0][i] = i;
		p2[i] = p2[i >> 1] + 1;
	}
	
	for (k = 1; (1 << k) <= pos_node; ++k) {
		int dif = 1 << (k - 1);
		for (i = 1; i + (1 << k) - 1 <= pos_node; ++i) {
			rmq[k][i] = rmq[k - 1][i];
			if (level[rmq[k][i]] > level[rmq[k - 1][i + dif]]) {
				rmq[k][i] = rmq[k - 1][i + dif];
			}
		}
	}
}