// generated at caterpillow.github.io/byot

#include <bits/stdc++.h>

using namespace std;
using ll = long long;

// custom monoid

const ll mod = 1e9 + 7;
const ll m = 27;
ll mpow(ll x, ll y) {
    ll res = 1;
    while (y) {
        if (y & 1) res = (res * x) % mod;
        x = (x * x) % mod;
        y >>= 1;
    }
    return res;
}

// You can implement your own monoid here for custom operations.
struct Value {
    ll hash, rhash, size;
    Value operator+(const Value& oth) const {
        Value res {};
        res.hash = ((hash * mpow(27, oth.size)) % mod + oth.hash) % mod;
        res.rhash = (rhash + (oth.rhash * mpow(27, size)) % mod) % mod;
        res.size = size + oth.size;
        return res;
    }
};

const Value vid = {0, 0, 0};

// end custom code

mt19937 mt(chrono::steady_clock::now().time_since_epoch().count());
using ptr = struct Node*;

struct Node {
    Value val;
    Value agg;

    int sz;
    int pri;
    ptr l, r;

    Node() {
        pri = mt();
        val = vid;
        agg = vid;
        sz = 1;
        l = r = nullptr;
    }

    Node(Value val) : val(val), agg(val) {
        pri = mt();
        sz = 1;
        l = r = nullptr;
    }

    ~Node() {
        delete l;
        delete r;
    }
};

int sz(ptr n) { return n ? n->sz : 0; };
Value agg(ptr n) { return n ? n->agg : vid; }

ptr pull(ptr n) {
    if (!n) return nullptr;
    n->sz = sz(n->l) + 1 + sz(n->r);
    n->agg = agg(n->l) + n->val + agg(n->r);
    return n;
}

ptr merge(ptr l, ptr r) {
    if (!l || !r) return l ? l : r;
    if (l->pri > r->pri) return l->r = merge(l->r, r), pull(l);
    else return r->l = merge(l, r->l), pull(r);
}

// [-inf, i) and [i, inf]
pair<ptr, ptr> spliti(ptr n, int i) {
    if (!n) return {nullptr, nullptr};
    if (i <= sz(n->l)) {
        auto [l, r] = spliti(n->l, i);
        n->l = r;
        return {l, pull(n)};
    } else {
        auto [l, r] = spliti(n->r, i - 1 - sz(n->l));
        n->r = l;
        return {pull(n), r};
    }
}

// cuts out [lo, hi)
tuple<ptr, ptr, ptr> spliti(ptr n, int lo, int hi) {
    auto [lm, r] = spliti(n, hi);
    auto [l, m] = spliti(lm, lo);
    return {l, m, r};
}

// inserts node such that it will be at index i. only insert single nodes
void insi(ptr& n, ptr it, int i) {
    if (!n) { n = it; return; }
    if (n->pri < it->pri) {
        auto [l, r] = spliti(n, i);
        it->l = l, it->r = r, n = it;
    } else if (i <= sz(n->l)) insi(n->l, it, i);
    else insi(n->r, it, i - 1 - sz(n->l));
    pull(n);
}

Value queryi(ptr n, int lo, int hi) {
    if (!n || lo >= sz(n) || hi <= 0) return vid;
    if (lo <= 0 && sz(n) <= hi) return n->agg;
    return queryi(n->l, lo, hi) + (lo <= sz(n->l) && sz(n->l) < hi ? n->val : vid) + queryi(n->r, lo - 1 - sz(n->l), hi - 1 - sz(n->l));
}

void heapify(ptr n) {
    if (!n) return;
    ptr mx = n;
    if (n->l && n->l->pri > mx->pri) mx = n->l;
    if (n->r && n->r->pri > mx->pri) mx = n->r;
    if (mx != n) swap(n->pri, mx->pri), heapify(mx);
}

ptr build(vector<ptr>& ns, int l = 0, int r = -69) {
    if (r == -69) r = (int) ns.size() - 1;
    if (l > r) return nullptr;
    if (l == r) return ns[l];
    int m = (l + r) / 2;
    ns[m]->l = build(ns, l, m - 1);
    ns[m]->r = build(ns, m + 1, r);
    heapify(ns[m]);
    return pull(ns[m]);
}

/*

- include merge
- include 3-way split by index (and size, and split by index)
- include point insert by index 
- include build (and heapify)
- include range queries (and range aggregates)

*/

main() {
    cin.tie(0)->sync_with_stdio(0);
    
    int n, q; cin >> n >> q;
    string st; cin >> st;

    vector<ptr> ns(n);
    for (int i = 0; i < n; i++) {
        ns[i] = new Node({st[i] - 'a' + 1, st[i] - 'a' + 1, 1});
    }
    ptr treap = build(ns);

    while (q--) {
        int t; cin >> t;
        if (t == 1) {
            int lo, hi; cin >> lo >> hi;
            auto [l, m, r] = spliti(treap, lo - 1, hi);
            treap = merge(l, r);
            delete m;
        }
        if (t == 2) {
            char c;
            int p;
            cin >> c >> p;
            insi(treap, new Node {{c - 'a' + 1, c - 'a' + 1, 1}}, p - 1);
        }
        if (t == 3) {
            int l, r; cin >> l >> r;
            Value res = queryi(treap, l - 1, r);
            cout << (res.hash == res.rhash ? "yes" : "no") << '\n';
        }
    }
    delete treap;
}
