#include <bits/stdc++.h>
using namespace std;
template <class AT, class UT>
struct SegmentTree {
  AT I;
  UT NU, v;
  int N, l, r;
  vector<AT> A;
  vector<UT> U;
  vector<int> L, R;
  virtual AT& op(const AT& i, const AT& j, AT& r) = 0;
  virtual AT& us(int p, const UT& v) = 0;
  SegmentTree(int N, AT I = 0, UT NU = INT_MIN)
      : I(I), NU(NU), N(N), A(4 * N, I), U(4 * N, NU), L(4 * N), R(4 * N) {
    build(1, 0, N - 1);
  }
  void build(int p, int i, int j) {
    L[p] = i;
    R[p] = j;
    if (i != j) {
      build(2 * p, i, (i + j) / 2);
      build(2 * p + 1, (i + j) / 2 + 1, j);
    }
  }
  SegmentTree(const vector<AT>& data, AT I = 0, UT NU = INT_MIN)
      : I(I),
        NU(NU),
        N(data.size()),
        A(4 * N),
        U(4 * N, NU),
        L(4 * N),
        R(4 * N) {}
  AT& build(int p, int i, int j, const vector<AT>& data) {
    L[p] = i;
    R[p] = j;
    if (i == j) return A[p] = data[i];
    return op(build(2 * p, i, (i + j) / 2, data),
              build(2 * p + 1, (i + j) / 2 + 1, j, data), A[p]);
  }
  AT update(int p) {
    if (l <= L[p] && R[p] <= r) return us(p, v);
    if (R[p] < l || r < L[p] || L[p] == R[p]) return v == NU ? I : A[p];
    us(2 * p, U[p]);
    us(2 * p + 1, U[p]);
    U[p] = NU;
    AT r;
    op(update(2 * p), update(2 * p + 1), r);
    if (v != NU) A[p] = r;
    return r;
  }
  AT update(int i, int j, UT k) {
    l = i;
    r = j;
    v = k;
    return j < i ? I : update(1);
  }
  AT query(int i, int j) { return update(i, j, NU); }
};
struct ARR : SegmentTree<char, char> {
  ARR(const vector<char>& data) : SegmentTree(data, 10, 10) {
    if (N) build(1, 0, N - 1, data);
  }
  char& op(const char& i, const char& j, char& r) {
    return r = (i == I ? j : i);
  }
  char& us(int p, const char& v) {
    if (v != NU) {
      A[p] = v;
      U[p] = v;
    }
    return A[p];
  }
};
int n, m, k;
string s;
struct CTS : SegmentTree<array<int, 100>, array<int, 5> > {
  CTS(const vector<array<int, 100> >& data)
      : SegmentTree(data, array<int, 100>(), {INT_MIN, 0, 0, 0, 0}) {
    if (N) build(1, 0, N - 1, data);
  }
  array<int, 100>& op(const array<int, 100>& i, const array<int, 100>& j,
                      array<int, 100>& r) {
    r = i;
    for (int c = 0; c < 100; c++) r[c] += j[c];
    return r;
  }
  array<int, 100>& us(int p, const array<int, 5>& v) {
    if (v != NU) {
      array<int, 5> t = v;
      if (L[p] != t[3]) t[0] = t[1];
      if (R[p] != t[4]) t[2] = t[1];
      A[p].fill(0);
      int w = R[p] - L[p] + 1;
      if (w == 1) {
        A[p][k * t[0] + t[2]] = 1;
      } else {
        A[p][k * t[0] + t[1]]++;
        A[p][k * t[1] + t[2]]++;
        A[p][t[1] * (1 + k)] += w - 2;
      }
      U[p] = v;
    }
    return A[p];
  }
};
int main() {
  int i;
  char cc;
  ios::sync_with_stdio(false);
  cin >> n >> m >> k >> s;
  for (int c = 0; c < n; c++) s[c] -= 'a';
  array<int, 100> t;
  vector<array<int, 100> > data(n - 1);
  for (int c = 0; c < n - 1; c++) data[c][k * s[c] + s[c + 1]] = 1;
  ARR chars(vector<char>(s.begin(), s.end()));
  CTS cts(data);
  int x, y;
  for (int c = 0; c < m; c++) {
    cin >> i;
    if (i == 1) {
      cin >> x >> y >> cc;
      x--;
      y--;
      cc -= 'a';
      chars.update(x, y, cc);
      x--;
      if (x < 0) x++;
      if (y == n - 1) y--;
      cts.update(x, y,
                 {chars.query(x, x), cc, chars.query(y + 1, y + 1), x, y});
    } else {
      cin >> s;
      int r = 1;
      t = cts.query(0, n - 2);
      for (int i = 0; i < s.size(); i++)
        for (int j = i; j < s.size(); j++)
          r += t[(s[j] - 'a') * k + s[i] - 'a'];
      cout << r << endl;
    }
  }
  return 0;
}
