// Success consists of going from failure to failure without loss of enthusiasm
#include <bits/stdc++.h>
using namespace std;
#define nl '\n'
mt19937 rng(1008);
const int MX = 2e5;
const int MOD = 1e9 + 7, base = 37;
vector<int> pows = {1};
// treap node
struct node {
int val, pri, sz = 1;
int hash, rhash; // left to right hash, and right to left (reversed) hash
node *left, *right;
node(int v) {
hash = rhash = val = v; // initalize hashes and value of node
pri = (rng() % MOD); // set priority of the node
left = right = NULL; // set children to null
}
};
// getter methods
int get_size(node* t) { return t ? t->sz : 0; };
int get_hash(node* t) { return t ? t->hash : 0; };
int get_rhash(node* t) { return t ? t->rhash : 0; };
// function to combine hashs
int combine_hash(int hasha, int hashb, int blen) {
return ((hasha * 1LL * pows[blen]) % MOD + hashb) % MOD;
}
node* comp(node* t) {
if (t == NULL) return t;
// maintaining the size of the treap
t->sz = 1 + get_size(t->left) + get_size(t->right);
// maintaining left to right hash
t->hash = get_hash(t->left);
t->hash = combine_hash(t->hash, t->val, 1);
t->hash = combine_hash(t->hash, get_hash(t->right), get_size(t->right));
// maintaining right to left hash
t->rhash = get_rhash(t->right);
t->rhash = combine_hash(t->rhash, t->val, 1);
t->rhash = combine_hash(t->rhash, get_rhash(t->left), get_size(t->left));
return t;
}
// merge two treaps (l is to the left of r)
node* merge(node* l, node* r) {
// if one of the nodes is null, return the other
if (!l || !r) return l ? l : r;
// whichever is higher priority is the root
if (l->pri > r->pri) {
// l is root, merge right child with r
l->right = merge(l->right, r);
return comp(l);
} else {
// r is root, merge left child with l
r->left = merge(l, r->left);
return comp(r);
}
}
// split treap into 2 -> with "amount" nodes in the left half
pair<node*, node*> split_by_amount(node* t, int amount) {
if (t == NULL) return {t, t};
// if left child has enough nodes, then recurse on left child
if (get_size(t->left) >= amount) {
auto [l, r] = split_by_amount(t->left, amount);
t->left = r; // left child will become the excess from the split
return {l, comp(t)};
} else { // otherwise, recurse on right child (taking all the nodes to the left)
auto [l, r] = split_by_amount(t->right, amount - get_size(t->left) - 1);
t->right = l; // right child will become the part taken from the right subtree
return {comp(t), r};
}
}
// split treap into 2 -> with values with < v and >= v separately
pair<node*, node*> split_by_value(node* t, int v) {
if (t == NULL) return {t, t};
// if it is greater or equal to the value, then everything in the right child is larger too
if (t->val >= v) {
// recurse on the left child
auto [l, r] = split_by_value(t->left, v);
t->left = r; // left child becomes the part not taken from the left child
return {l, comp(t)};
} else { // otherwise, everything in the left subtree (and this node) is taken
// recurse on the right subtree
auto [l, r] = split_by_value(t->right, v);
t->right = l; // right child becomes the part taken from the right child
return {comp(t), r};
}
}
int main() {
cin.tie(0)->sync_with_stdio(0);
// building array of powers of base (for hashing)
for(int i = 0; i < MX; i++)
pows.push_back((pows.back() * 1LL * base) % MOD);
int N, Q; cin >> N >> Q;
node* T = NULL;
for(int i = 0; i < N; i++) {
char c; cin >> c;
// building treap
T = merge(T, new node(c - 'a' + 1));
}
while(Q--) {
int t; cin >> t; --t;
if (t == 0) {
int l, r; cin >> l >> r; --l, --r;
// split first l elements from rest
auto [L, M] = split_by_amount(T, l);
// split first r elements from rest
auto [m, R] = split_by_amount(M, r - l + 1);
// merge elements before l and elements after r
T = merge(L, R);
}
if (t == 1) {
int p; char c; cin >> c >> p; --p;
// split first p elements from rest
auto [L, R] = split_by_amount(T, p);
// merge first p elements with a node with a value of c
// then merge with rest of treap
T = merge(L, merge(new node(c - 'a' + 1), R));
}
if (t == 2) {
int l, r; cin >> l >> r; --l, --r;
// split first l elements from rest
auto [L, M] = split_by_amount(T, l);
// split first r elements from rest
auto [m, R] = split_by_amount(M, r - l + 1);
// m is the treap that corresponds to the range [l, r] in the string
// check if the left to right hash is equal to the right to left hash
bool pali = get_hash(m) == get_rhash(m);
T = merge(L, merge(m, R));
cout << (pali ? "yes" : "no") << nl;
}
}
exit(0-0);
}
// Breathe In, Breathe Out