use std::vec::Vec;
use std::collections::HashMap;

fn read<T: std::str::FromStr>() -> T {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).ok();
    s.trim().parse().ok().unwrap()
}

fn read_vec<T: std::str::FromStr>() -> Vec<T> {
    read::<String>().split_whitespace().map(|e| e.parse().ok().unwrap()).collect()
}


fn read_touple3() -> (usize, usize, usize) {
  let v: Vec<String> = read::<String>().split_whitespace().map(|e| e.parse().ok().unwrap()).collect();
  assert_eq!(v.len(), 3);

  let mut res = (0, 0, 0);
  res.0 = v[0].parse().ok().unwrap();
  res.1 = v[1].parse().ok().unwrap();
  res.1 = res.1 - 1;

  if res.0 == 2 {
     res.2 = v[2].parse::<usize>().ok().unwrap() - 1;
  } else {
    res.2 = v[2].chars().collect::<Vec<char>>()[0] as usize - 'a' as usize;
  }
  res
}

fn main() {
    let n: usize = read();
    let s: Vec<usize> = read::<String>().chars().map(|c| c as usize - 'a' as usize).collect();
    let Q: usize = read();
    let mut querry = vec![];
    for _ in 0..Q {
      let t = read_touple3();
      querry.push(t);
    }

    let mut st = SegmentTree::new(&s);
    for &(t, i, j) in &querry {
      if t == 1 {
         st.update(i, j)
      } else {
        let res = st.get(i, j+1).iter().fold(0, |acc, &x| if x > 0 { acc + 1 } else { acc });
        println!("{}", res);
      }
    }
}


#[derive(Debug, PartialEq)]
struct SegmentTree {
    n: usize, //元の配列に一番近い2の冪乗を格納する
    tree: Vec<Vec<usize>>,
}

fn add(x: &Vec<usize>, y: &Vec<usize>) -> Vec<usize> {
    assert!(x.len() == 26);
    assert!(y.len() == 26);
    let mut res = vec![0; x.len()];

    for i in 0..x.len() {
        res[i] = x[i] + y[i];
    }

    res
}

impl SegmentTree {
    // 元の配列をセグメント木で表現
    // 2^nが元の配列のサイズ以上になる最小のnをNとする
    // O(N)
    fn new(v: &Vec<usize>) -> SegmentTree {
        let mut n: usize = 1;
        // セグメント木の葉の要素数
        while n < v.len() {
            n *= 2;
        }

        // セグメント木を表現するベクター
        let mut tree = vec![vec![0; 26]; 2 * n - 1];

        // 葉から順に値を格納していく
        // まず最下段に値を格納する
        for i in 0..v.len() {
            tree[i + n - 1][v[i]] = 1;
        }

        // 最下段の次の下段から値を書くのしていく
        for i in (0..(n - 1)).rev() {
            tree[i] = add(&tree[2 * i + 1], &tree[2 * i + 2]);
        }

        SegmentTree { n: n, tree: tree }
    }

    fn update(&mut self, i: usize, val: usize) {
        let mut m = i + self.n - 1;
        for j in 0..self.tree[m].len() {
            if self.tree[m][j] == 1 && j == val {
                return;
            } else if self.tree[m][j] == 1 {
                self.tree[m][j] = 0;
            } else if j == val {
                self.tree[m][j] = 1;
            }
        }
        while m > 0 {
            m = (m - 1) / 2;
            self.tree[m] = add(&self.tree[2 * m + 1], &self.tree[2 * m + 2]);
        }
    }

    fn get(&self, i: usize, j: usize) -> Vec<usize> {
        fn get_aux(
            t: &SegmentTree,
            a: usize,
            b: usize,
            k: usize,
            l: usize,
            r: usize,
        ) -> Vec<usize> {
            if r <= a || b <= l {
                return vec![0; 26];
            }

            if a <= l && r <= b {
                return t.tree[k].clone();
            }

            let vl = get_aux(t, a, b, 2 * k + 1, l, (l + r) / 2);
            let vr = get_aux(t, a, b, 2 * k + 2, (l + r) / 2, r);

            return add(&vl, &vr);
        }

        return get_aux(&self, i, j, 0, 0, self.n);
    }
}

Measured memory_bytes: 268272