#[allow(unused_macros)]
macro_rules! debug {
    ($($a:expr),*) => {
        #[cfg(debug_assertions)]
        writeln!(&mut std::io::stderr(), concat!("[DEBUG] ", $(stringify!($a), "={:?} "),*), $($a),*);
    }
}

#[allow(unused_imports)]
use std::cmp::{min, max};

#[allow(unused_imports)]
use std::io::Write;
type I = usize;

pub struct Scanner<R> {
    reader: R,
}
impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .reader
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n')
            .take_while(|&b| b != b' ' && b != b'\n')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn read_vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}

fn main() {
    let stdin = std::io::stdin();
    let mut sc = Scanner { reader: stdin };

    let n:usize = sc.read();
    let q:usize = sc.read();
    let s:Vec<char> = sc.chars();

    let mut qs = vec![];
    for _ in 0..q {
        let op: char = sc.read();
        let r: usize = sc.read();
        let mut w = '-';
        if op == '+' {
            w = sc.read();
        }
        qs.push((op, r, w));
    }

    const INF: usize = 1 << 32;
    let mut next = vec![vec![INF; n+1]; 26];
    for c in 0..26 {
        for i in (0..n).rev() {
            next[c][i] = next[c][i+1];
            if s[i] == (c as u8 + 'a' as u8) as char {
                next[c][i] = i;
            }
        }
    }

    const M: usize = 5;
    let mut dp = vec![vec![vec![INF; M]; M]; M];
    dp[0][0][0] = 0;

    let mut x: Vec<char> = vec![];
    let mut y: Vec<char> = vec![];
    let mut z: Vec<char> = vec![];

    for &(op, r, w) in qs.iter() {
        if op == '+' {
            if r == 1 {
                x.push(w);
                let i = x.len();
                for j in 0..M {
                    for k in 0..M {
                        if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
                            let c = (x[i-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
                        }
                        if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
                            let c = (y[j-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
                        }
                        if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
                            let c = (z[k-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
                        }
                    }
                }
            } else if r == 2 {
                y.push(w);
                let j = y.len();
                for i in 0..M {
                    for k in 0..M {
                        if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
                            let c = (x[i-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
                        }
                        if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
                            let c = (y[j-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
                        }
                        if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
                            let c = (z[k-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
                        }
                    }
                }
            } else {
                z.push(w);
                let k = z.len();
                for i in 0..M {
                    for j in 0..M {
                        if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
                            let c = (x[i-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
                        }
                        if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
                            let c = (y[j-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
                        }
                        if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
                            let c = (z[k-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
                        }
                    }
                }
            }
        } else {
            if r == 1 {
                x.pop();
            } else if r == 2 {
                y.pop();
            } else {
                z.pop();
            }
        }
        let ans = dp[x.len()][y.len()][z.len()];
        if ans != INF {
            println!("{}", "YES");
        } else {
            println!("{}", "NO");
        }
    }
}


#[allow(unused_macros)]
macro_rules! debug {
    ($($a:expr),*) => {
        #[cfg(debug_assertions)]
        writeln!(&mut std::io::stderr(), concat!("[DEBUG] ", $(stringify!($a), "={:?} "),*), $($a),*);
    }
}

#[allow(unused_imports)]
use std::cmp::{min, max};

#[allow(unused_imports)]
use std::io::Write;
type I = usize;
type Chars = Vec<char>;
const INF: usize = 1 << 20;

pub struct Scanner<R> {
    reader: R,
}
impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .reader
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n')
            .take_while(|&b| b != b' ' && b != b'\n')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn read_vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}

fn update(dp: &mut Vec<Vec<Vec<usize>>>, i:usize, j:usize, k:usize, x:&Chars, y:&Chars, z:&Chars, next: &Vec<Vec<usize>>) {
    if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
        let c = (x[i-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
    }
    if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
        let c = (y[j-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
    }
    if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
        let c = (z[k-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
    }
}

fn solve() {
    let stdin = std::io::stdin();
    let mut sc = Scanner { reader: stdin };

    let n:usize = sc.read();
    let q:usize = sc.read();

    println!("{} {}", n, q);
    return;
    let s:Vec<char> = sc.chars();

    let mut qs = vec![];
    for _ in 0..q {
        let op: char = sc.read();
        let r: usize = sc.read();
        let mut w = '-';
        if op == '+' {
            w = sc.read();
        }
        qs.push((op, r, w));
    }

    let mut next = vec![vec![INF; n+1]; 26];
    for c in 0..26 {
        for i in (0..n).rev() {
            next[c][i] = next[c][i+1];
            if s[i] == (c as u8 + 'a' as u8) as char {
                next[c][i] = i;
            }
        }
    }

    const M: usize = 251;
    let mut dp = vec![vec![vec![INF; M]; M]; M];
    dp[0][0][0] = 0;

    let mut x: Vec<char> = vec![];
    let mut y: Vec<char> = vec![];
    let mut z: Vec<char> = vec![];

    for &(op, r, w) in qs.iter() {
        if op == '+' {
            if r == 1 {
                x.push(w);
                let i = x.len();
                for j in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else if r == 2 {
                y.push(w);
                let j = y.len();
                for i in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else {
                z.push(w);
                let k = z.len();
                for i in 0..M {
                    for j in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            }
        } else {
            if r == 1 {
                x.pop();
            } else if r == 2 {
                y.pop();
            } else {
                z.pop();
            }
        }
        let ans = dp[x.len()][y.len()][z.len()];
        if ans != INF {
            println!("{}", "YES");
        } else {
            println!("{}", "NO");
        }
    }
}

fn main() {
    let stack_size = 104_857_600; // 100 MB
    let thd = std::thread::Builder::new().stack_size(stack_size);
    thd.spawn(|| solve()).unwrap().join().unwrap();
}


#[allow(unused_macros)]
macro_rules! debug {
    ($($a:expr),*) => {
        #[cfg(debug_assertions)]
        writeln!(&mut std::io::stderr(), concat!("[DEBUG] ", $(stringify!($a), "={:?} "),*), $($a),*);
    }
}

#[allow(unused_imports)]
use std::cmp::{min, max};

#[allow(unused_imports)]
use std::io::Write;
type I = usize;
type Chars = Vec<char>;
const INF: usize = 1 << 20;

pub struct Scanner<R> {
    reader: R,
}
impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .reader
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n')
            .take_while(|&b| b != b' ' && b != b'\n')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn read_vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}

fn update(dp: &mut Vec<Vec<Vec<usize>>>, i:usize, j:usize, k:usize, x:&Chars, y:&Chars, z:&Chars, next: &Vec<Vec<usize>>) {
    if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
        let c = (x[i-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
    }
    if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
        let c = (y[j-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
    }
    if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
        let c = (z[k-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
    }
}

fn solve() {
    let stdin = std::io::stdin();
    let mut sc = Scanner { reader: stdin };

    let n:usize = sc.read();
    let q:usize = sc.read();
    let s:Vec<char> = sc.chars();

    let mut qs = vec![];
    for _ in 0..q {
        let op: char = sc.read();
        let r: usize = sc.read();
        let mut w = '-';
        if op == '+' {
            w = sc.read();
        }
        qs.push((op, r, w));
    }

    let mut next = vec![vec![INF; n+1]; 26];
    for c in 0..26 {
        for i in (0..n).rev() {
            next[c][i] = next[c][i+1];
            if s[i] == (c as u8 + 'a' as u8) as char {
                next[c][i] = i;
            }
        }
    }

    const M: usize = 251;
    let mut dp = vec![vec![vec![INF; M]; M]; M];
    dp[0][0][0] = 0;

    let mut x: Vec<char> = vec![];
    let mut y: Vec<char> = vec![];
    let mut z: Vec<char> = vec![];

    for &(op, r, w) in qs.iter() {
        if op == '+' {
            if r == 1 {
                x.push(w);
                let i = x.len();
                for j in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else if r == 2 {
                y.push(w);
                let j = y.len();
                for i in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else {
                z.push(w);
                let k = z.len();
                for i in 0..M {
                    for j in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            }
        } else {
            if r == 1 {
                x.pop();
            } else if r == 2 {
                y.pop();
            } else {
                z.pop();
            }
        }
        let ans = dp[x.len()][y.len()][z.len()];
        if ans != INF {
            println!("{}", "YES");
        } else {
            println!("{}", "NO");
        }
    }
}

fn main() {
    let stack_size = 104_857_600; // 100 MB
    let thd = std::thread::Builder::new().stack_size(stack_size);
    thd.spawn(|| solve()).unwrap().join().unwrap();
}


#[allow(unused_macros)]
macro_rules! debug {
    ($($a:expr),*) => {
        #[cfg(debug_assertions)]
        writeln!(&mut std::io::stderr(), concat!("[DEBUG] ", $(stringify!($a), "={:?} "),*), $($a),*);
    }
}

#[allow(unused_imports)]
use std::cmp::{min, max};

#[allow(unused_imports)]
use std::io::Write;
type I = usize;

pub struct Scanner<R> {
    reader: R,
}
impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .reader
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n')
            .take_while(|&b| b != b' ' && b != b'\n')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn read_vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}

fn main() {
    let stdin = std::io::stdin();
    let mut sc = Scanner { reader: stdin };

    let n:usize = sc.read();
    let q:usize = sc.read();
    let s:Vec<char> = sc.chars();

    let mut qs = vec![];
    for _ in 0..q {
        let op: char = sc.read();
        let r: usize = sc.read();
        let mut w = '-';
        if op == '+' {
            w = sc.read();
        }
        qs.push((op, r, w));
    }

    const INF: usize = 1 << 31;
    let mut next = vec![vec![INF; n+1]; 26];
    for c in 0..26 {
        for i in (0..n).rev() {
            next[c][i] = next[c][i+1];
            if s[i] == (c as u8 + 'a' as u8) as char {
                next[c][i] = i;
            }
        }
    }

    const M: usize = 5;
    let mut dp = vec![vec![vec![INF; M]; M]; M];
    dp[0][0][0] = 0;

    let mut x: Vec<char> = vec![];
    let mut y: Vec<char> = vec![];
    let mut z: Vec<char> = vec![];

    for &(op, r, w) in qs.iter() {
        if op == '+' {
            if r == 1 {
                x.push(w);
                let i = x.len();
                for j in 0..M {
                    for k in 0..M {
                        if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
                            let c = (x[i-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
                        }
                        if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
                            let c = (y[j-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
                        }
                        if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
                            let c = (z[k-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
                        }
                    }
                }
            } else if r == 2 {
                y.push(w);
                let j = y.len();
                for i in 0..M {
                    for k in 0..M {
                        if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
                            let c = (x[i-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
                        }
                        if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
                            let c = (y[j-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
                        }
                        if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
                            let c = (z[k-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
                        }
                    }
                }
            } else {
                z.push(w);
                let k = z.len();
                for i in 0..M {
                    for j in 0..M {
                        if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
                            let c = (x[i-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
                        }
                        if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
                            let c = (y[j-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
                        }
                        if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
                            let c = (z[k-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
                        }
                    }
                }
            }
        } else {
            if r == 1 {
                x.pop();
            } else if r == 2 {
                y.pop();
            } else {
                z.pop();
            }
        }
        let ans = dp[x.len()][y.len()][z.len()];
        if ans != INF {
            println!("{}", "YES");
        } else {
            println!("{}", "NO");
        }
    }
}


#[allow(unused_macros)]
macro_rules! debug {
    ($($a:expr),*) => {
        #[cfg(debug_assertions)]
        writeln!(&mut std::io::stderr(), concat!("[DEBUG] ", $(stringify!($a), "={:?} "),*), $($a),*);
    }
}

#[allow(unused_imports)]
use std::cmp::{min, max};

#[allow(unused_imports)]
use std::io::Write;
type I = usize;
type Chars = Vec<char>;
const INF: usize = 1 << 30;

pub struct Scanner<R> {
    reader: R,
}
impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .reader
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n')
            .take_while(|&b| b != b' ' && b != b'\n')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn read_vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}

fn update(dp: &mut Vec<Vec<Vec<usize>>>, i:usize, j:usize, k:usize, x:&Chars, y:&Chars, z:&Chars, next: &Vec<Vec<usize>>) {
    if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
        let c = (x[i-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
    }
    if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
        let c = (y[j-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
    }
    if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
        let c = (z[k-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
    }
}

fn solve() {
    let stdin = std::io::stdin();
    let mut sc = Scanner { reader: stdin };

    let n:usize = sc.read();
    let q:usize = sc.read();
    let s:Vec<char> = sc.chars();

    let mut qs = vec![];
    for _ in 0..q {
        let op: char = sc.read();
        let r: usize = sc.read();
        let mut w = '-';
        if op == '+' {
            w = sc.read();
        }
        qs.push((op, r, w));
    }

    let mut next = vec![vec![INF; n+1]; 26];
    for c in 0..26 {
        for i in (0..n).rev() {
            next[c][i] = next[c][i+1];
            if s[i] == (c as u8 + 'a' as u8) as char {
                next[c][i] = i;
            }
        }
    }

    const M: usize = 251;
    let mut dp = vec![vec![vec![INF; M]; M]; M];
    dp[0][0][0] = 0;

    let mut x: Vec<char> = vec![];
    let mut y: Vec<char> = vec![];
    let mut z: Vec<char> = vec![];

    for &(op, r, w) in qs.iter() {
        if op == '+' {
            if r == 1 {
                x.push(w);
                let i = x.len();
                for j in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else if r == 2 {
                y.push(w);
                let j = y.len();
                for i in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else {
                z.push(w);
                let k = z.len();
                for i in 0..M {
                    for j in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            }
        } else {
            if r == 1 {
                x.pop();
            } else if r == 2 {
                y.pop();
            } else {
                z.pop();
            }
        }
        let ans = dp[x.len()][y.len()][z.len()];
        if ans != INF {
            println!("{}", "YES");
        } else {
            println!("{}", "NO");
        }
    }
}

fn main() {
    let stack_size = 104_857_600; // 100 MB
    let thd = std::thread::Builder::new().stack_size(stack_size);
    thd.spawn(|| solve()).unwrap().join().unwrap();
}


#[allow(unused_macros)]
macro_rules! debug {
    ($($a:expr),*) => {
        #[cfg(debug_assertions)]
        writeln!(&mut std::io::stderr(), concat!("[DEBUG] ", $(stringify!($a), "={:?} "),*), $($a),*);
    }
}

#[allow(unused_imports)]
use std::cmp::{min, max};

#[allow(unused_imports)]
use std::io::Write;
type I = usize;

pub struct Scanner<R> {
    reader: R,
}
impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .reader
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n')
            .take_while(|&b| b != b' ' && b != b'\n')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn read_vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}

fn main() {
    let stdin = std::io::stdin();
    let mut sc = Scanner { reader: stdin };

    let n:usize = sc.read();
    let q:usize = sc.read();
    let s:Vec<char> = sc.chars();

    let mut qs = vec![];
    for _ in 0..q {
        let op: char = sc.read();
        let r: usize = sc.read();
        let mut w = '-';
        if op == '+' {
            w = sc.read();
        }
        qs.push((op, r, w));
    }

    const INF: usize = 1 << 30;
    let mut next = vec![vec![INF; n+1]; 26];
    for c in 0..26 {
        for i in (0..n).rev() {
            next[c][i] = next[c][i+1];
            if s[i] == (c as u8 + 'a' as u8) as char {
                next[c][i] = i;
            }
        }
    }

    const M: usize = 5;
    let mut dp = vec![vec![vec![INF; M]; M]; M];
    dp[0][0][0] = 0;

    let mut x: Vec<char> = vec![];
    let mut y: Vec<char> = vec![];
    let mut z: Vec<char> = vec![];

    for &(op, r, w) in qs.iter() {
        if op == '+' {
            if r == 1 {
                x.push(w);
                let i = x.len();
                for j in 0..M {
                    for k in 0..M {
                        if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
                            let c = (x[i-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
                        }
                        if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
                            let c = (y[j-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
                        }
                        if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
                            let c = (z[k-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
                        }
                    }
                }
            } else if r == 2 {
                y.push(w);
                let j = y.len();
                for i in 0..M {
                    for k in 0..M {
                        if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
                            let c = (x[i-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
                        }
                        if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
                            let c = (y[j-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
                        }
                        if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
                            let c = (z[k-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
                        }
                    }
                }
            } else {
                z.push(w);
                let k = z.len();
                for i in 0..M {
                    for j in 0..M {
                        if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
                            let c = (x[i-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
                        }
                        if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
                            let c = (y[j-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
                        }
                        if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
                            let c = (z[k-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
                        }
                    }
                }
            }
        } else {
            if r == 1 {
                x.pop();
            } else if r == 2 {
                y.pop();
            } else {
                z.pop();
            }
        }
        let ans = dp[x.len()][y.len()][z.len()];
        if ans != INF {
            println!("{}", "YES");
        } else {
            println!("{}", "NO");
        }
    }
}


#[allow(unused_macros)]
macro_rules! debug {
    ($($a:expr),*) => {
        #[cfg(debug_assertions)]
        writeln!(&mut std::io::stderr(), concat!("[DEBUG] ", $(stringify!($a), "={:?} "),*), $($a),*);
    }
}

#[allow(unused_imports)]
use std::cmp::{min, max};

#[allow(unused_imports)]
use std::io::Write;
type I = usize;
type Chars = Vec<char>;
const INF: usize = 1 << 20;

pub struct Scanner<R> {
    reader: R,
}
impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .reader
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n')
            .take_while(|&b| b != b' ' && b != b'\n')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn read_vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}

fn update(dp: &mut Vec<Vec<Vec<usize>>>, i:usize, j:usize, k:usize, x:&Chars, y:&Chars, z:&Chars, next: &Vec<Vec<usize>>) {
    if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
        let c = (x[i-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
    }
    if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
        let c = (y[j-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
    }
    if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
        let c = (z[k-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
    }
}

fn solve() {
    let stdin = std::io::stdin();
    let mut sc = Scanner { reader: stdin };

    let n:usize = sc.read();
    let q:usize = sc.read();
    let s:Vec<char> = sc.chars();

    let mut qs = vec![];
    for _ in 0..q {
        let op: char = sc.read();
        let r: usize = sc.read();
        let mut w = '-';
        if op == '+' {
            w = sc.read();
        }
        qs.push((op, r, w));
    }

    println!("{:?}", qs);
    return;

    let mut next = vec![vec![INF; n+1]; 26];
    for c in 0..26 {
        for i in (0..n).rev() {
            next[c][i] = next[c][i+1];
            if s[i] == (c as u8 + 'a' as u8) as char {
                next[c][i] = i;
            }
        }
    }

    const M: usize = 251;
    let mut dp = vec![vec![vec![INF; M]; M]; M];
    dp[0][0][0] = 0;

    let mut x: Vec<char> = vec![];
    let mut y: Vec<char> = vec![];
    let mut z: Vec<char> = vec![];

    for &(op, r, w) in qs.iter() {
        if op == '+' {
            if r == 1 {
                x.push(w);
                let i = x.len();
                for j in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else if r == 2 {
                y.push(w);
                let j = y.len();
                for i in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else {
                z.push(w);
                let k = z.len();
                for i in 0..M {
                    for j in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            }
        } else {
            if r == 1 {
                x.pop();
            } else if r == 2 {
                y.pop();
            } else {
                z.pop();
            }
        }
        let ans = dp[x.len()][y.len()][z.len()];
        if ans != INF {
            println!("{}", "YES");
        } else {
            println!("{}", "NO");
        }
    }
}

fn main() {
    let stack_size = 104_857_600; // 100 MB
    let thd = std::thread::Builder::new().stack_size(stack_size);
    thd.spawn(|| solve()).unwrap().join().unwrap();
}


#[allow(unused_macros)]
macro_rules! debug {
    ($($a:expr),*) => {
        #[cfg(debug_assertions)]
        writeln!(&mut std::io::stderr(), concat!("[DEBUG] ", $(stringify!($a), "={:?} "),*), $($a),*);
    }
}

#[allow(unused_imports)]
use std::cmp::{min, max};

#[allow(unused_imports)]
use std::io::Write;
type I = usize;
type Chars = Vec<char>;
const INF: usize = 1 << 20;

pub struct Scanner<R> {
    reader: R,
}
impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .reader
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n')
            .take_while(|&b| b != b' ' && b != b'\n')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn read_vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}

fn update(dp: &mut Vec<Vec<Vec<usize>>>, i:usize, j:usize, k:usize, x:&Chars, y:&Chars, z:&Chars, next: &Vec<Vec<usize>>) {
    if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
        let c = (x[i-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
    }
    if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
        let c = (y[j-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
    }
    if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
        let c = (z[k-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
    }
}

fn solve() {
    let stdin = std::io::stdin();
    let mut sc = Scanner { reader: stdin };

    let n:usize = sc.read();
    let q:usize = sc.read();
    let s:Vec<char> = sc.chars();

    println!("{:?}", "OK");
    return;

    let mut qs = vec![];
    for _ in 0..q {
        let op: char = sc.read();
        let r: usize = sc.read();
        let mut w = '-';
        if op == '+' {
            w = sc.read();
        }
        qs.push((op, r, w));
    }

    println!("{:?}", qs);
    return;

    let mut next = vec![vec![INF; n+1]; 26];
    for c in 0..26 {
        for i in (0..n).rev() {
            next[c][i] = next[c][i+1];
            if s[i] == (c as u8 + 'a' as u8) as char {
                next[c][i] = i;
            }
        }
    }

    const M: usize = 251;
    let mut dp = vec![vec![vec![INF; M]; M]; M];
    dp[0][0][0] = 0;

    let mut x: Vec<char> = vec![];
    let mut y: Vec<char> = vec![];
    let mut z: Vec<char> = vec![];

    for &(op, r, w) in qs.iter() {
        if op == '+' {
            if r == 1 {
                x.push(w);
                let i = x.len();
                for j in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else if r == 2 {
                y.push(w);
                let j = y.len();
                for i in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else {
                z.push(w);
                let k = z.len();
                for i in 0..M {
                    for j in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            }
        } else {
            if r == 1 {
                x.pop();
            } else if r == 2 {
                y.pop();
            } else {
                z.pop();
            }
        }
        let ans = dp[x.len()][y.len()][z.len()];
        if ans != INF {
            println!("{}", "YES");
        } else {
            println!("{}", "NO");
        }
    }
}

fn main() {
    let stack_size = 104_857_600; // 100 MB
    let thd = std::thread::Builder::new().stack_size(stack_size);
    thd.spawn(|| solve()).unwrap().join().unwrap();
}


#[allow(unused_macros)]
macro_rules! debug {
    ($($a:expr),*) => {
        #[cfg(debug_assertions)]
        writeln!(&mut std::io::stderr(), concat!("[DEBUG] ", $(stringify!($a), "={:?} "),*), $($a),*);
    }
}

#[allow(unused_imports)]
use std::cmp::{min, max};

#[allow(unused_imports)]
use std::io::Write;
type I = usize;
type Chars = Vec<char>;
const INF: usize = 1 << 20;

pub struct Scanner<R> {
    reader: R,
}
impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf: String = self
            .reader
            .by_ref()
            .bytes()
            .map(|b| b.unwrap() as char)
            .skip_while(|c| c.is_whitespace())
            .take_while(|c| !c.is_whitespace())
            .collect();
        buf.parse::<T>()
            .ok()
            .expect("Parse error.")
    }
    pub fn read_vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}

fn update(dp: &mut Vec<Vec<Vec<usize>>>, i:usize, j:usize, k:usize, x:&Chars, y:&Chars, z:&Chars, next: &Vec<Vec<usize>>) {
    dp[i][j][k] = INF;
    if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
        let c = (x[i-1] as u8 - 'a'  as u8) as usize;
        let next = next[c][ dp[i-1][j][k] ];
        if next != INF {
            dp[i][j][k] = min(dp[i][j][k], next + 1);
        }
    }
    if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
        let c = (y[j-1] as u8 - 'a'  as u8) as usize;
        let next = next[c][ dp[i][j-1][k] ];
        if next != INF {
            dp[i][j][k] = min(dp[i][j][k], next + 1);
        }
    }
    if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
        let c = (z[k-1] as u8 - 'a'  as u8) as usize;
        let next = next[c][ dp[i][j][k-1] ];
        if next != INF {
            dp[i][j][k] = min(dp[i][j][k], next + 1);
        }
    }
}

fn solve() {
    let stdin = std::io::stdin();
    let mut sc = Scanner { reader: stdin.lock() };

    let n:usize = sc.read();
    let q:usize = sc.read();
    let s:Vec<char> = sc.chars();

    let mut qs = vec![];
    for _ in 0..q {
        let op: char = sc.read();
        let r: usize = sc.read();
        let mut w = '-';
        if op == '+' {
            w = sc.read();
        }
        qs.push((op, r, w));
    }

    let mut next = vec![vec![INF; n+1]; 26];
    for c in 0..26 {
        for i in (0..n).rev() {
            next[c][i] = next[c][i+1];
            if s[i] == (c as u8 + 'a' as u8) as char {
                next[c][i] = i;
            }
        }
    }

    const M: usize = 251;
    let mut dp = vec![vec![vec![INF; M]; M]; M];
    dp[0][0][0] = 0;

    let mut x: Vec<char> = vec![];
    let mut y: Vec<char> = vec![];
    let mut z: Vec<char> = vec![];

    let mut cnt = 0;
    for &(op, r, w) in qs.iter() {
        if op == '+' {
            if r == 1 {
                x.push(w);
                let i = x.len();
                for j in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else if r == 2 {
                y.push(w);
                let j = y.len();
                for i in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else {
                z.push(w);
                let k = z.len();
                for i in 0..M {
                    for j in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            }
        } else {
            if r == 1 {
                x.pop();
            } else if r == 2 {
                y.pop();
            } else {
                z.pop();
            }
        }
        let ans = dp[x.len()][y.len()][z.len()];
        cnt += 1;
        if ans != INF {
            println!("{}", "YES");
        } else {
            println!("{}", "NO");
        }
    }
}

fn main() {
    let stack_size = 104_857_600; // 100 MB
    let thd = std::thread::Builder::new().stack_size(stack_size);
    thd.spawn(|| solve()).unwrap().join().unwrap();
}


#[allow(unused_macros)]
macro_rules! debug {
    ($($a:expr),*) => {
        #[cfg(debug_assertions)]
        writeln!(&mut std::io::stderr(), concat!("[DEBUG] ", $(stringify!($a), "={:?} "),*), $($a),*);
    }
}

#[allow(unused_imports)]
use std::cmp::{min, max};

#[allow(unused_imports)]
use std::io::Write;
type I = usize;
type Chars = Vec<char>;
const INF: usize = 1 << 30;

pub struct Scanner<R> {
    reader: R,
}
impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .reader
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n')
            .take_while(|&b| b != b' ' && b != b'\n')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn read_vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}

fn update(dp: &mut Vec<Vec<Vec<usize>>>, i:usize, j:usize, k:usize, x:&Chars, y:&Chars, z:&Chars, next: &Vec<Vec<usize>>) {
    if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
        let c = (x[i-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
    }
    if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
        let c = (y[j-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
    }
    if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
        let c = (z[k-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
    }
}

fn main() {
    let stdin = std::io::stdin();
    let mut sc = Scanner { reader: stdin };

    let n:usize = sc.read();
    let q:usize = sc.read();
    let s:Vec<char> = sc.chars();

    let mut qs = vec![];
    for _ in 0..q {
        let op: char = sc.read();
        let r: usize = sc.read();
        let mut w = '-';
        if op == '+' {
            w = sc.read();
        }
        qs.push((op, r, w));
    }

    let mut next = vec![vec![INF; n+1]; 26];
    for c in 0..26 {
        for i in (0..n).rev() {
            next[c][i] = next[c][i+1];
            if s[i] == (c as u8 + 'a' as u8) as char {
                next[c][i] = i;
            }
        }
    }

    const M: usize = 251;
    let mut dp = vec![vec![vec![INF; M]; M]; M];
    dp[0][0][0] = 0;

    let mut x: Vec<char> = vec![];
    let mut y: Vec<char> = vec![];
    let mut z: Vec<char> = vec![];

    for &(op, r, w) in qs.iter() {
        println!("{}", "OK");
        return;
        if op == '+' {
            if r == 1 {
                x.push(w);
                let i = x.len();
                for j in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else if r == 2 {
                y.push(w);
                let j = y.len();
                for i in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else {
                z.push(w);
                let k = z.len();
                for i in 0..M {
                    for j in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            }
        } else {
            if r == 1 {
                x.pop();
            } else if r == 2 {
                y.pop();
            } else {
                z.pop();
            }
        }
        let ans = dp[x.len()][y.len()][z.len()];
        if ans != INF {
            println!("{}", "YES");
        } else {
            println!("{}", "NO");
        }
    }
}


#[allow(unused_macros)]
macro_rules! debug {
    ($($a:expr),*) => {
        #[cfg(debug_assertions)]
        writeln!(&mut std::io::stderr(), concat!("[DEBUG] ", $(stringify!($a), "={:?} "),*), $($a),*);
    }
}

#[allow(unused_imports)]
use std::cmp::{min, max};

#[allow(unused_imports)]
use std::io::Write;
type I = usize;
type Chars = Vec<char>;
const INF: usize = 1 << 20;

pub struct Scanner<R> {
    reader: R,
}
impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .reader
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n')
            .take_while(|&b| b != b' ' && b != b'\n')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn read_vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}

fn update(dp: &mut Vec<Vec<Vec<usize>>>, i:usize, j:usize, k:usize, x:&Chars, y:&Chars, z:&Chars, next: &Vec<Vec<usize>>) {
    if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
        let c = (x[i-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
    }
    if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
        let c = (y[j-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
    }
    if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
        let c = (z[k-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
    }
}

fn solve() {
    let stdin = std::io::stdin();
    let mut sc = Scanner { reader: stdin.lock() };

    let n:usize = sc.read();
    let q:usize = sc.read();
    let s:Vec<char> = sc.chars();

    let mut qs = vec![];
    for _ in 0..q {
        let op: char = sc.read();
        let r: usize = sc.read();
        let mut w = '-';
        if op == '+' {
            w = sc.read();
        }
        qs.push((op, r, w));
    }

    let mut next = vec![vec![INF; n+1]; 26];
    for c in 0..26 {
        for i in (0..n).rev() {
            next[c][i] = next[c][i+1];
            if s[i] == (c as u8 + 'a' as u8) as char {
                next[c][i] = i;
            }
        }
    }

    const M: usize = 251;
    let mut dp = vec![vec![vec![INF; M]; M]; M];
    dp[0][0][0] = 0;

    let mut x: Vec<char> = vec![];
    let mut y: Vec<char> = vec![];
    let mut z: Vec<char> = vec![];

    for &(op, r, w) in qs.iter() {
        if op == '+' {
            if r == 1 {
                x.push(w);
                let i = x.len();
                for j in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else if r == 2 {
                y.push(w);
                let j = y.len();
                for i in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else {
                z.push(w);
                let k = z.len();
                for i in 0..M {
                    for j in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            }
        } else {
            if r == 1 {
                x.pop();
            } else if r == 2 {
                y.pop();
            } else {
                z.pop();
            }
        }
        let ans = dp[x.len()][y.len()][z.len()];
        if ans != INF {
            println!("{}", "YES");
        } else {
            println!("{}", "NO");
        }
    }
}

fn main() {
    let stack_size = 104_857_600; // 100 MB
    let thd = std::thread::Builder::new().stack_size(stack_size);
    thd.spawn(|| solve()).unwrap().join().unwrap();
}


#[allow(unused_macros)]
macro_rules! debug {
    ($($a:expr),*) => {
        #[cfg(debug_assertions)]
        writeln!(&mut std::io::stderr(), concat!("[DEBUG] ", $(stringify!($a), "={:?} "),*), $($a),*);
    }
}

#[allow(unused_imports)]
use std::cmp::{min, max};

#[allow(unused_imports)]
use std::io::Write;
type I = usize;

pub struct Scanner<R> {
    reader: R,
}
impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf = self
            .reader
            .by_ref()
            .bytes()
            .map(|b| b.unwrap())
            .skip_while(|&b| b == b' ' || b == b'\n')
            .take_while(|&b| b != b' ' && b != b'\n')
            .collect::<Vec<_>>();
        unsafe { std::str::from_utf8_unchecked(&buf) }
            .parse()
            .ok()
            .expect("Parse error.")
    }
    pub fn read_vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}

fn main() {
    let stdin = std::io::stdin();
    let mut sc = Scanner { reader: stdin };

    let n:usize = sc.read();
    let q:usize = sc.read();
    let s:Vec<char> = sc.chars();

    let mut qs = vec![];
    for _ in 0..q {
        let op: char = sc.read();
        let r: usize = sc.read();
        let mut w = '-';
        if op == '+' {
            w = sc.read();
        }
        qs.push((op, r, w));
    }

    const INF: usize = 1 << 30;
    let mut next = vec![vec![INF; n+1]; 26];
    for c in 0..26 {
        for i in (0..n).rev() {
            next[c][i] = next[c][i+1];
            if s[i] == (c as u8 + 'a' as u8) as char {
                next[c][i] = i;
            }
        }
    }

    const M: usize = 250;
    let mut dp = vec![vec![vec![INF; M]; M]; M];
    dp[0][0][0] = 0;

    let mut x: Vec<char> = vec![];
    let mut y: Vec<char> = vec![];
    let mut z: Vec<char> = vec![];

    for &(op, r, w) in qs.iter() {
        if op == '+' {
            if r == 1 {
                x.push(w);
                let i = x.len();
                for j in 0..M {
                    for k in 0..M {
                        if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
                            let c = (x[i-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
                        }
                        if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
                            let c = (y[j-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
                        }
                        if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
                            let c = (z[k-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
                        }
                    }
                }
            } else if r == 2 {
                y.push(w);
                let j = y.len();
                for i in 0..M {
                    for k in 0..M {
                        if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
                            let c = (x[i-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
                        }
                        if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
                            let c = (y[j-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
                        }
                        if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
                            let c = (z[k-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
                        }
                    }
                }
            } else {
                z.push(w);
                let k = z.len();
                for i in 0..M {
                    for j in 0..M {
                        if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
                            let c = (x[i-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
                        }
                        if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
                            let c = (y[j-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
                        }
                        if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
                            let c = (z[k-1] as u8 - 'a'  as u8) as usize;
                            dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
                        }
                    }
                }
            }
        } else {
            if r == 1 {
                x.pop();
            } else if r == 2 {
                y.pop();
            } else {
                z.pop();
            }
        }
        let ans = dp[x.len()][y.len()][z.len()];
        if ans != INF {
            println!("{}", "YES");
        } else {
            println!("{}", "NO");
        }
    }
}


#[allow(unused_macros)]
macro_rules! debug {
    ($($a:expr),*) => {
        #[cfg(debug_assertions)]
        writeln!(&mut std::io::stderr(), concat!("[DEBUG] ", $(stringify!($a), "={:?} "),*), $($a),*);
    }
}

#[allow(unused_imports)]
use std::cmp::{min, max};

#[allow(unused_imports)]
use std::io::Write;
type I = usize;
type Chars = Vec<char>;
const INF: usize = 1 << 20;

pub struct Scanner<R> {
    reader: R,
}
impl<R: std::io::Read> Scanner<R> {
    pub fn read<T: std::str::FromStr>(&mut self) -> T {
        use std::io::Read;
        let buf: String = self
            .reader
            .by_ref()
            .bytes()
            .map(|b| b.unwrap() as char)
            .skip_while(|c| c.is_whitespace())
            .take_while(|c| !c.is_whitespace())
            .collect();
        buf.parse::<T>()
            .ok()
            .expect("Parse error.")
    }
    pub fn read_vec<T: std::str::FromStr>(&mut self, n: usize) -> Vec<T> {
        (0..n).map(|_| self.read()).collect()
    }
    pub fn chars(&mut self) -> Vec<char> {
        self.read::<String>().chars().collect()
    }
}

fn update(dp: &mut Vec<Vec<Vec<usize>>>, i:usize, j:usize, k:usize, x:&Chars, y:&Chars, z:&Chars, next: &Vec<Vec<usize>>) {
    if i > 0 && i <= x.len() && dp[i-1][j][k] != INF {
        let c = (x[i-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i-1][j][k] ] + 1);
    }
    if j > 0 && j <= y.len() && dp[i][j-1][k] != INF {
        let c = (y[j-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j-1][k] ] + 1);
    }
    if k > 0 && k <= z.len() && dp[i][j][k-1] != INF {
        let c = (z[k-1] as u8 - 'a'  as u8) as usize;
        dp[i][j][k] = min(dp[i][j][k], next[c][ dp[i][j][k-1] ] + 1);
    }
}

fn solve() {
    let stdin = std::io::stdin();
    let mut sc = Scanner { reader: stdin.lock() };

    let n:usize = sc.read();
    let q:usize = sc.read();
    let s:Vec<char> = sc.chars();

    let mut qs = vec![];
    for _ in 0..q {
        let op: char = sc.read();
        let r: usize = sc.read();
        let mut w = '-';
        if op == '+' {
            w = sc.read();
        }
        qs.push((op, r, w));
    }

    let mut next = vec![vec![INF; n+1]; 26];
    for c in 0..26 {
        for i in (0..n).rev() {
            next[c][i] = next[c][i+1];
            if s[i] == (c as u8 + 'a' as u8) as char {
                next[c][i] = i;
            }
        }
    }

    const M: usize = 251;
    let mut dp = vec![vec![vec![INF; M]; M]; M];
    dp[0][0][0] = 0;

    let mut x: Vec<char> = vec![];
    let mut y: Vec<char> = vec![];
    let mut z: Vec<char> = vec![];

    for &(op, r, w) in qs.iter() {
        if op == '+' {
            if r == 1 {
                x.push(w);
                let i = x.len();
                for j in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else if r == 2 {
                y.push(w);
                let j = y.len();
                for i in 0..M {
                    for k in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            } else {
                z.push(w);
                let k = z.len();
                for i in 0..M {
                    for j in 0..M {
                        update(&mut dp, i, j, k, &x, &y, &z, &next);
                    }
                }
            }
        } else {
            if r == 1 {
                x.pop();
            } else if r == 2 {
                y.pop();
            } else {
                z.pop();
            }
        }
        let ans = dp[x.len()][y.len()][z.len()];
        if ans != INF {
            println!("{}", "YES");
        } else {
            println!("{}", "NO");
        }
    }
}

fn main() {
    let stack_size = 104_857_600; // 100 MB
    let thd = std::thread::Builder::new().stack_size(stack_size);
    thd.spawn(|| solve()).unwrap().join().unwrap();
}


import java.util.*

fun main() {
    val (n, q) = readInts()
    val word = readLn()

    val arbiter = Arbiter(word)

    output {
        repeat(q) {
            val t = readStrings()
            when(t[0]) {
                "+" -> arbiter.append(t[1].toInt(), t[2].first())
                "-" -> arbiter.removeLast(t[1].toInt())
            }
            println(if(arbiter.isGood) "YES" else "NO")
        }
    }
}

const val MAX_RELIGION_LENGTH = 250

class Arbiter(val word: String) {

    private val religions = List(3) { ArrayList<Char>(MAX_RELIGION_LENGTH) }

    private inline fun religion(index: Int) = religions[index - 1]

    private val N =
        List(26) {
            val c = 'a' + it
            var acc = word.length
            List(word.length) {
                val i = word.lastIndex - it
                if(word[i] == c) acc = i
                acc
            }.asReversed()
        }

    private inline fun firstAfter(char: Char, position: Int) =
        if(position >= word.length) word.length else N[char - 'a'][position]

    private val D = List(MAX_RELIGION_LENGTH+1) {
        List(MAX_RELIGION_LENGTH+1) {
            IntArray(MAX_RELIGION_LENGTH+1)
        }
    }.also { it[0][0][0] = -1 }

    private inline fun updateD(n1: Int, n2: Int, n3: Int) {
        D[n1][n2][n3] = minOf(
            if(n1 > 0) firstAfter(religion(1)[n1-1], D[n1-1][n2][n3] + 1) else word.length,
            if(n2 > 0) firstAfter(religion(2)[n2-1], D[n1][n2-1][n3] + 1) else word.length,
            if(n3 > 0) firstAfter(religion(3)[n3-1], D[n1][n2][n3-1] + 1) else word.length
        )
    }

    fun append(religion: Int, char: Char) {
        religion(religion).add(char)
        when(religion) {
            1 -> {
                for(x in 0..religion(2).size) {
                    for(y in 0..religion(3).size) {
                        updateD(religion(1).size, x, y)
                    }
                }
            }
            2 -> {
                for(x in 0..religion(1).size) {
                    for(y in 0..religion(3).size) {
                        updateD(x, religion(2).size, y)
                    }
                }
            }
            3 -> {
                for(x in 0..religion(1).size) {
                    for(y in 0..religion(2).size) {
                        updateD(x, y, religion(3).size)
                    }
                }
            }
        }
    }

    fun removeLast(religion: Int) {
        religion(religion).run { removeAt(lastIndex) }
    }

    val isGood: Boolean get() = D[religion(1).size][religion(2).size][religion(3).size] < word.length
}

fun readLn() = readLine()!!
fun readInt() = readLn().toInt()
fun readDouble() = readLn().toDouble()
fun readLong() = readLn().toLong()
fun readStrings() = readLn().split(' ')
fun readInts() = readStrings().map { it.toInt() }
fun readDoubles() = readStrings().map { it.toDouble() }
fun readLongs() = readStrings().map { it.toLong() }

class Output {
    private val sb = StringBuilder()
    fun print(o: Any?) { sb.append(o) }
    fun println() { sb.append('\n') }
    fun println(o: Any?) { sb.append(o).append('\n') }
    @JvmName("_print") fun Any?.print() = print(this)
    @JvmName("_println") fun Any?.println() = println(this)
    fun nowPrint() { kotlin.io.print(sb) }
}
inline fun output(block: Output.()->Unit)
    { Output().apply(block).nowPrint() }


import java.util.*

fun main() {
    val (n, q) = readInts()
    val word = readLn()

    val arbiter = Arbiter(word)

    output {
        repeat(q) {
            val t = readStrings()
            when(t[0]) {
                "+" -> arbiter.append(t[1].toInt(), t[2].first())
                "-" -> arbiter.removeLast(t[1].toInt())
            }
            println(if(arbiter.isGood) "YES" else "NO")
        }
    }
}

const val MAX_RELIGION_LENGTH = 250

class Arbiter(val word: String) {

    private val religions = List(3) { ArrayList<Char>(MAX_RELIGION_LENGTH) }

    fun religion(index: Int) = religions[index - 1]

    private val N =
        List(26) {
            val c = 'a' + it
            var acc = word.length
            List(word.length) {
                val i = word.lastIndex - it
                if(word[i] == c) acc = i
                acc
            }.asReversed()
        }

    fun firstAfter(char: Char, position: Int) =
        if(position >= word.length) word.length else N[char - 'a'][position]

    private val D = List(MAX_RELIGION_LENGTH) {
        List(MAX_RELIGION_LENGTH) {
            IntArray(MAX_RELIGION_LENGTH)
        }
    }.also { it[0][0][0] = -1 }

    private fun updateD(n1: Int, n2: Int, n3: Int) {
        D[n1][n2][n3] = minOf(
            if(n1 > 0) firstAfter(religion(1)[n1-1], D[n1-1][n2][n3] + 1) else word.length,
            if(n2 > 0) firstAfter(religion(2)[n2-1], D[n1][n2-1][n3] + 1) else word.length,
            if(n3 > 0) firstAfter(religion(3)[n3-1], D[n1][n2][n3-1] + 1) else word.length
        )
    }

    fun append(religion: Int, char: Char) {
        religion(religion).add(char)
        when(religion) {
            1 -> {
                for(x in 0..religion(2).size) {
                    for(y in 0..religion(3).size) {
                        updateD(religion(1).size, x, y)
                    }
                }
            }
            2 -> {
                for(x in 0..religion(1).size) {
                    for(y in 0..religion(3).size) {
                        updateD(x, religion(2).size, y)
                    }
                }
            }
            3 -> {
                for(x in 0..religion(1).size) {
                    for(y in 0..religion(2).size) {
                        updateD(x, y, religion(3).size)
                    }
                }
            }
        }
    }

    fun removeLast(religion: Int) {
        religion(religion).run { removeAt(lastIndex) }
    }

    val isGood: Boolean get() = D[religion(1).size][religion(2).size][religion(3).size] < word.length
}

fun readLn() = readLine()!!
fun readInt() = readLn().toInt()
fun readDouble() = readLn().toDouble()
fun readLong() = readLn().toLong()
fun readStrings() = readLn().split(' ')
fun readInts() = readStrings().map { it.toInt() }
fun readDoubles() = readStrings().map { it.toDouble() }
fun readLongs() = readStrings().map { it.toLong() }

class Output {
    private val sb = StringBuilder()
    fun print(o: Any?) { sb.append(o) }
    fun println() { sb.append('\n') }
    fun println(o: Any?) { sb.append(o).append('\n') }
    @JvmName("_print") fun Any?.print() = print(this)
    @JvmName("_println") fun Any?.println() = println(this)
    fun nowPrint() { kotlin.io.print(sb) }
}
inline fun output(block: Output.()->Unit)
    { Output().apply(block).nowPrint() }


import java.util.*

fun main() {
    val (n, q) = readInts()
    val word = readLn()

    val arbiter = Arbiter(word)

    output {
        repeat(q) {
            val t = readStrings()
            when(t[0]) {
                "+" -> arbiter.append(t[1].toInt(), t[2].first())
                "-" -> arbiter.removeLast(t[1].toInt())
            }
            println(if(arbiter.isGood) "YES" else "NO")
        }
    }
}

const val MAX_RELIGION_LENGTH = 250

class Arbiter(val word: String) {

    private val religions = Array(3) { CharArray(MAX_RELIGION_LENGTH) }
    private val religionSizes = IntArray(3)

    //private inline fun religion(index: Int) = religions[index - 1]

    private val N =
        Array(26) {
            val c = 'a' + it
            var acc = word.length
            IntArray(word.length).apply {
                for(i in word.lastIndex downTo 0) {
                    if(word[i] == c) acc = i
                    this[i] = acc
                }
            }
        }

    private inline fun firstAfter(char: Char, position: Int) =
        if(position >= word.length) word.length else N[char - 'a'][position]

    private val D = Array(MAX_RELIGION_LENGTH+1) {
        Array(MAX_RELIGION_LENGTH+1) {
            IntArray(MAX_RELIGION_LENGTH+1)
        }
    }.also { it[0][0][0] = -1 }

    private inline fun updateD(n1: Int, n2: Int, n3: Int) {
        D[n1][n2][n3] = minOf(
            if(n1 > 0) firstAfter(religions[0][n1-1], D[n1-1][n2][n3] + 1) else word.length,
            if(n2 > 0) firstAfter(religions[1][n2-1], D[n1][n2-1][n3] + 1) else word.length,
            if(n3 > 0) firstAfter(religions[2][n3-1], D[n1][n2][n3-1] + 1) else word.length
        )
    }

    fun append(religion: Int, char: Char) {
        val i = religion - 1
        religions[i][religionSizes[i]] = char
        religionSizes[i]++
        when(i) {
            0 -> {
                for(x in 0..religionSizes[1]) {
                    for(y in 0..religionSizes[2]) {
                        updateD(religionSizes[0], x, y)
                    }
                }
            }
            1 -> {
                for(x in 0..religionSizes[0]) {
                    for(y in 0..religionSizes[2]) {
                        updateD(x, religionSizes[1], y)
                    }
                }
            }
            2 -> {
                for(x in 0..religionSizes[0]) {
                    for(y in 0..religionSizes[1]) {
                        updateD(x, y, religionSizes[2])
                    }
                }
            }
        }
    }

    fun removeLast(religion: Int) {
        religionSizes[religion-1] --
    }

    val isGood: Boolean get() = D[religionSizes[0]][religionSizes[1]][religionSizes[2]] < word.length
}

fun readLn() = readLine()!!
fun readInt() = readLn().toInt()
fun readDouble() = readLn().toDouble()
fun readLong() = readLn().toLong()
fun readStrings() = readLn().split(' ')
fun readInts() = readStrings().map { it.toInt() }
fun readDoubles() = readStrings().map { it.toDouble() }
fun readLongs() = readStrings().map { it.toLong() }

class Output {
    private val sb = StringBuilder()
    fun print(o: Any?) { sb.append(o) }
    fun println() { sb.append('\n') }
    fun println(o: Any?) { sb.append(o).append('\n') }
    @JvmName("_print") fun Any?.print() = print(this)
    @JvmName("_println") fun Any?.println() = println(this)
    fun nowPrint() { kotlin.io.print(sb) }
}
inline fun output(block: Output.()->Unit)
    { Output().apply(block).nowPrint() }


import java.util.*

fun main() {
    val (n, q) = readInts()
    val word = readLn()

    val arbiter = Arbiter(word)

    output {
        repeat(q) {
            val t = readStrings()
            when(t[0]) {
                "+" -> arbiter.append(t[1].toInt(), t[2].first())
                "-" -> arbiter.removeLast(t[1].toInt())
            }
            println(if(arbiter.isGood) "YES" else "NO")
        }
    }
}

const val MAX_RELIGION_LENGTH = 250

class Arbiter(val word: String) {

    private val religions = Array(3) { CharArray(MAX_RELIGION_LENGTH) }
    private val religionSizes = IntArray(3)

    private val N =
        Array(26) {
            val c = 'a' + it
            var acc = word.length
            IntArray(word.length).apply {
                for(i in word.lastIndex downTo 0) {
                    if(word[i] == c) acc = i
                    this[i] = acc
                }
            }
        }

    private inline fun firstAfter(char: Char, position: Int) =
        if(position >= word.length) word.length else N[char - 'a'][position]

    private val D = Array(MAX_RELIGION_LENGTH+1) {
        Array(MAX_RELIGION_LENGTH+1) {
            IntArray(MAX_RELIGION_LENGTH+1)
        }
    }.also { it[0][0][0] = -1 }

    private inline fun updateD(n1: Int, n2: Int, n3: Int) {
        D[n1][n2][n3] = minOf(
            if(n1 > 0) firstAfter(religions[0][n1-1], D[n1-1][n2][n3] + 1) else word.length,
            if(n2 > 0) firstAfter(religions[1][n2-1], D[n1][n2-1][n3] + 1) else word.length,
            if(n3 > 0) firstAfter(religions[2][n3-1], D[n1][n2][n3-1] + 1) else word.length
        )
    }

    fun append(religion: Int, char: Char) {
        val i = religion - 1
        religions[i][religionSizes[i]] = char
        religionSizes[i]++
        when(i) {
            0 -> {
                for(x in 0..religionSizes[1]) {
                    for(y in 0..religionSizes[2]) {
                        updateD(religionSizes[0], x, y)
                    }
                }
            }
            1 -> {
                for(x in 0..religionSizes[0]) {
                    for(y in 0..religionSizes[2]) {
                        updateD(x, religionSizes[1], y)
                    }
                }
            }
            2 -> {
                for(x in 0..religionSizes[0]) {
                    for(y in 0..religionSizes[1]) {
                        updateD(x, y, religionSizes[2])
                    }
                }
            }
        }
    }

    fun removeLast(religion: Int) {
        religionSizes[religion-1] --
    }

    val isGood: Boolean get() = D[religionSizes[0]][religionSizes[1]][religionSizes[2]] < word.length
}

fun readLn() = readLine()!!
fun readInt() = readLn().toInt()
fun readDouble() = readLn().toDouble()
fun readLong() = readLn().toLong()
fun readStrings() = readLn().split(' ')
fun readInts() = readStrings().map { it.toInt() }
fun readDoubles() = readStrings().map { it.toDouble() }
fun readLongs() = readStrings().map { it.toLong() }

class Output {
    private val sb = StringBuilder()
    fun print(o: Any?) { sb.append(o) }
    fun println() { sb.append('\n') }
    fun println(o: Any?) { sb.append(o).append('\n') }
    @JvmName("_print") fun Any?.print() = print(this)
    @JvmName("_println") fun Any?.println() = println(this)
    fun nowPrint() { kotlin.io.print(sb) }
}
inline fun output(block: Output.()->Unit)
    { Output().apply(block).nowPrint() }


import java.io.PrintWriter
import java.util.StringTokenizer

fun main() {
    val (n, q) = readInts(2)
    val word = readLn()

    val arbiter = Arbiter(word)

    output {
        repeat(q) {
            val t = read()
            when(t) {
                "+" -> arbiter.append(readInt(), read()[0])
                "-" -> arbiter.removeLast(readInt())
            }
            println(if(arbiter.isGood) "YES" else "NO")
        }
    }
}

const val MAX_RELIGION_LENGTH = 250

class Arbiter(val word: String) {

    val wordLength = word.length

    private val religions = Array(3) { CharArray(MAX_RELIGION_LENGTH) }
    private val religionSizes = IntArray(3)

    private val N =
        Array(26) {
            val c = 'a' + it
            var acc = wordLength
            IntArray(wordLength).apply {
                for(i in wordLength-1 downTo 0) {
                    if(word[i] == c) acc = i
                    this[i] = acc
                }
            }
        }

    private inline fun firstAfter(char: Char, position: Int) =
        if(position >= wordLength) wordLength else N[char - 'a'][position]

    private val D = Array(MAX_RELIGION_LENGTH+1) {
        Array(MAX_RELIGION_LENGTH+1) {
            IntArray(MAX_RELIGION_LENGTH+1)
        }
    }.also { it[0][0][0] = -1 }

    private inline fun updateD(n1: Int, n2: Int, n3: Int) {
        D[n1][n2][n3] = minOf(
            if(n1 > 0) firstAfter(religions[0][n1-1], D[n1-1][n2][n3] + 1) else wordLength,
            if(n2 > 0) firstAfter(religions[1][n2-1], D[n1][n2-1][n3] + 1) else wordLength,
            if(n3 > 0) firstAfter(religions[2][n3-1], D[n1][n2][n3-1] + 1) else wordLength
        )
    }

    fun append(religion: Int, char: Char) {
        val i = religion - 1
        religions[i][religionSizes[i]] = char
        religionSizes[i]++
        when(i) {
            0 -> {
                for(x in 0..religionSizes[1]) {
                    for(y in 0..religionSizes[2]) {
                        updateD(religionSizes[0], x, y)
                    }
                }
            }
            1 -> {
                for(x in 0..religionSizes[0]) {
                    for(y in 0..religionSizes[2]) {
                        updateD(x, religionSizes[1], y)
                    }
                }
            }
            2 -> {
                for(x in 0..religionSizes[0]) {
                    for(y in 0..religionSizes[1]) {
                        updateD(x, y, religionSizes[2])
                    }
                }
            }
        }
    }

    fun removeLast(religion: Int) {
        religionSizes[religion-1] --
    }

    val isGood: Boolean get() = D[religionSizes[0]][religionSizes[1]][religionSizes[2]] < wordLength
}

/** IO code start */
@JvmField val _reader = System.`in`.bufferedReader()
fun readLine(): String? = _reader.readLine()
fun readLn() = _reader.readLine()!!
@JvmField var _tokenizer: StringTokenizer = StringTokenizer("")
fun read(): String {
    while (_tokenizer.hasMoreTokens().not()) _tokenizer = StringTokenizer(_reader.readLine() ?: return "", " ")
    return _tokenizer.nextToken()
}
fun readInt() = read().toInt()
fun readDouble() = read().toDouble()
fun readLong() = read().toLong()
fun readStrings(n: Int) = List(n) { read() }
fun readInts(n: Int) = List(n) { read().toInt() }
fun readDoubles(n: Int) = List(n) { read().toDouble() }
fun readLongs(n: Int) = List(n) { read().toLong() }

@JvmField val _writer = PrintWriter(System.out, false)
inline fun output(block: PrintWriter.()->Unit) { _writer.apply(block).flush() }
fun iprintln(o: Any?) { println(o) } // immediate println for interactive, bypasses output{} blocks


import java.util.*

fun main() {
    val (n, q) = readInts()
    val word = readLn()

    val arbiter = Arbiter(word)

    output {
        repeat(q) {
            val t = readStrings()
            when(t[0]) {
                "+" -> arbiter.append(t[1].toInt(), t[2].first())
                "-" -> arbiter.removeLast(t[1].toInt())
            }
            println(if(arbiter.isGood) "YES" else "NO")
        }
    }
}

const val MAX_RELIGION_LENGTH = 250

class Arbiter(val word: String) {

    private val religions = Array(3) { ArrayList<Char>(MAX_RELIGION_LENGTH) }

    private inline fun religion(index: Int) = religions[index - 1]

    private val N =
        Array(26) {
            val c = 'a' + it
            var acc = word.length
            IntArray(word.length).apply {
                for(i in word.lastIndex downTo 0) {
                    if(word[i] == c) acc = i
                    this[i] = acc
                }
            }
        }

    private inline fun firstAfter(char: Char, position: Int) =
        if(position >= word.length) word.length else N[char - 'a'][position]

    private val D = Array(MAX_RELIGION_LENGTH+1) {
        Array(MAX_RELIGION_LENGTH+1) {
            IntArray(MAX_RELIGION_LENGTH+1)
        }
    }.also { it[0][0][0] = -1 }

    private inline fun updateD(n1: Int, n2: Int, n3: Int) {
        D[n1][n2][n3] = minOf(
            if(n1 > 0) firstAfter(religion(1)[n1-1], D[n1-1][n2][n3] + 1) else word.length,
            if(n2 > 0) firstAfter(religion(2)[n2-1], D[n1][n2-1][n3] + 1) else word.length,
            if(n3 > 0) firstAfter(religion(3)[n3-1], D[n1][n2][n3-1] + 1) else word.length
        )
    }

    fun append(religion: Int, char: Char) {
        religion(religion).add(char)
        when(religion) {
            1 -> {
                for(x in 0..religion(2).size) {
                    for(y in 0..religion(3).size) {
                        updateD(religion(1).size, x, y)
                    }
                }
            }
            2 -> {
                for(x in 0..religion(1).size) {
                    for(y in 0..religion(3).size) {
                        updateD(x, religion(2).size, y)
                    }
                }
            }
            3 -> {
                for(x in 0..religion(1).size) {
                    for(y in 0..religion(2).size) {
                        updateD(x, y, religion(3).size)
                    }
                }
            }
        }
    }

    fun removeLast(religion: Int) {
        religion(religion).run { removeAt(lastIndex) }
    }

    val isGood: Boolean get() = D[religion(1).size][religion(2).size][religion(3).size] < word.length
}

fun readLn() = readLine()!!
fun readInt() = readLn().toInt()
fun readDouble() = readLn().toDouble()
fun readLong() = readLn().toLong()
fun readStrings() = readLn().split(' ')
fun readInts() = readStrings().map { it.toInt() }
fun readDoubles() = readStrings().map { it.toDouble() }
fun readLongs() = readStrings().map { it.toLong() }

class Output {
    private val sb = StringBuilder()
    fun print(o: Any?) { sb.append(o) }
    fun println() { sb.append('\n') }
    fun println(o: Any?) { sb.append(o).append('\n') }
    @JvmName("_print") fun Any?.print() = print(this)
    @JvmName("_println") fun Any?.println() = println(this)
    fun nowPrint() { kotlin.io.print(sb) }
}
inline fun output(block: Output.()->Unit)
    { Output().apply(block).nowPrint() }


import java.util.*

fun main() {
    val (n, q) = readInts()
    val word = readLn()

    val arbiter = Arbiter(word)

    output {
        repeat(q) {
            val t = readStrings()
            when(t[0]) {
                "+" -> arbiter.append(t[1].toInt(), t[2].first())
                "-" -> arbiter.removeLast(t[1].toInt())
            }
            println(if(arbiter.isGood) "YES" else "NO")
        }
    }
}

const val MAX_RELIGION_LENGTH = 250

class Arbiter(val word: String) {

    private val religions = List(3) { ArrayList<Char>(MAX_RELIGION_LENGTH) }

    fun religion(index: Int) = religions[index - 1]

    private val N =
        List(26) {
            val c = 'a' + it
            var acc = word.length
            List(word.length) {
                val i = word.lastIndex - it
                if(word[i] == c) acc = i
                acc
            }.asReversed()
        }

    fun firstAfter(char: Char, position: Int) =
        if(position >= word.length) word.length else N[char - 'a'][position]

    private val D = List(MAX_RELIGION_LENGTH+1) {
        List(MAX_RELIGION_LENGTH+1) {
            IntArray(MAX_RELIGION_LENGTH+1)
        }
    }.also { it[0][0][0] = -1 }

    private fun updateD(n1: Int, n2: Int, n3: Int) {
        D[n1][n2][n3] = minOf(
            if(n1 > 0) firstAfter(religion(1)[n1-1], D[n1-1][n2][n3] + 1) else word.length,
            if(n2 > 0) firstAfter(religion(2)[n2-1], D[n1][n2-1][n3] + 1) else word.length,
            if(n3 > 0) firstAfter(religion(3)[n3-1], D[n1][n2][n3-1] + 1) else word.length
        )
    }

    fun append(religion: Int, char: Char) {
        religion(religion).add(char)
        when(religion) {
            1 -> {
                for(x in 0..religion(2).size) {
                    for(y in 0..religion(3).size) {
                        updateD(religion(1).size, x, y)
                    }
                }
            }
            2 -> {
                for(x in 0..religion(1).size) {
                    for(y in 0..religion(3).size) {
                        updateD(x, religion(2).size, y)
                    }
                }
            }
            3 -> {
                for(x in 0..religion(1).size) {
                    for(y in 0..religion(2).size) {
                        updateD(x, y, religion(3).size)
                    }
                }
            }
        }
    }

    fun removeLast(religion: Int) {
        religion(religion).run { removeAt(lastIndex) }
    }

    val isGood: Boolean get() = D[religion(1).size][religion(2).size][religion(3).size] < word.length
}

fun readLn() = readLine()!!
fun readInt() = readLn().toInt()
fun readDouble() = readLn().toDouble()
fun readLong() = readLn().toLong()
fun readStrings() = readLn().split(' ')
fun readInts() = readStrings().map { it.toInt() }
fun readDoubles() = readStrings().map { it.toDouble() }
fun readLongs() = readStrings().map { it.toLong() }

class Output {
    private val sb = StringBuilder()
    fun print(o: Any?) { sb.append(o) }
    fun println() { sb.append('\n') }
    fun println(o: Any?) { sb.append(o).append('\n') }
    @JvmName("_print") fun Any?.print() = print(this)
    @JvmName("_println") fun Any?.println() = println(this)
    fun nowPrint() { kotlin.io.print(sb) }
}
inline fun output(block: Output.()->Unit)
    { Output().apply(block).nowPrint() }


import java.util.*

fun main() {
    val (n, q) = readInts()
    val word = readLn()

    val arbiter = Arbiter(word)

    output {
        repeat(q) {
            val t = readStrings()
            when(t[0]) {
                "+" -> arbiter.append(t[1].toInt(), t[2].first())
                "-" -> arbiter.removeLast(t[1].toInt())
            }
            println(if(arbiter.isGood) "YES" else "NO")
        }
    }
}

const val MAX_RELIGION_LENGTH = 250

class Arbiter(val word: String) {

    private val religions = List(3) { ArrayList<Char>(MAX_RELIGION_LENGTH) }

    inline fun religion(index: Int) = religions[index - 1]

    private val N =
        List(26) {
            val c = 'a' + it
            var acc = word.length
            List(word.length) {
                val i = word.lastIndex - it
                if(word[i] == c) acc = i
                acc
            }.asReversed()
        }

    inline fun firstAfter(char: Char, position: Int) =
        if(position >= word.length) word.length else N[char - 'a'][position]

    private val D = List(MAX_RELIGION_LENGTH+1) {
        List(MAX_RELIGION_LENGTH+1) {
            IntArray(MAX_RELIGION_LENGTH+1)
        }
    }.also { it[0][0][0] = -1 }

    private inline fun updateD(n1: Int, n2: Int, n3: Int) {
        D[n1][n2][n3] = minOf(
            if(n1 > 0) firstAfter(religion(1)[n1-1], D[n1-1][n2][n3] + 1) else word.length,
            if(n2 > 0) firstAfter(religion(2)[n2-1], D[n1][n2-1][n3] + 1) else word.length,
            if(n3 > 0) firstAfter(religion(3)[n3-1], D[n1][n2][n3-1] + 1) else word.length
        )
    }

    fun append(religion: Int, char: Char) {
        religion(religion).add(char)
        when(religion) {
            1 -> {
                for(x in 0..religion(2).size) {
                    for(y in 0..religion(3).size) {
                        updateD(religion(1).size, x, y)
                    }
                }
            }
            2 -> {
                for(x in 0..religion(1).size) {
                    for(y in 0..religion(3).size) {
                        updateD(x, religion(2).size, y)
                    }
                }
            }
            3 -> {
                for(x in 0..religion(1).size) {
                    for(y in 0..religion(2).size) {
                        updateD(x, y, religion(3).size)
                    }
                }
            }
        }
    }

    fun removeLast(religion: Int) {
        religion(religion).run { removeAt(lastIndex) }
    }

    val isGood: Boolean get() = D[religion(1).size][religion(2).size][religion(3).size] < word.length
}

fun readLn() = readLine()!!
fun readInt() = readLn().toInt()
fun readDouble() = readLn().toDouble()
fun readLong() = readLn().toLong()
fun readStrings() = readLn().split(' ')
fun readInts() = readStrings().map { it.toInt() }
fun readDoubles() = readStrings().map { it.toDouble() }
fun readLongs() = readStrings().map { it.toLong() }

class Output {
    private val sb = StringBuilder()
    fun print(o: Any?) { sb.append(o) }
    fun println() { sb.append('\n') }
    fun println(o: Any?) { sb.append(o).append('\n') }
    @JvmName("_print") fun Any?.print() = print(this)
    @JvmName("_println") fun Any?.println() = println(this)
    fun nowPrint() { kotlin.io.print(sb) }
}
inline fun output(block: Output.()->Unit)
    { Output().apply(block).nowPrint() }


import java.util.*

fun main() {
    val (n, q) = readInts()
    val word = readLn()

    val arbiter = Arbiter(word)

    output {
        repeat(q) {
            val t = readStrings()
            when(t[0]) {
                "+" -> arbiter.append(t[1].toInt(), t[2].first())
                "-" -> arbiter.removeLast(t[1].toInt())
            }
            println(if(arbiter.isGood) "YES" else "NO")
        }
    }
}

const val MAX_RELIGION_LENGTH = 250

class Arbiter(val word: String) {

    val wordLength = word.length

    private val religions = Array(3) { CharArray(MAX_RELIGION_LENGTH) }
    private val religionSizes = IntArray(3)

    private val N =
        Array(26) {
            val c = 'a' + it
            var acc = wordLength
            IntArray(wordLength).apply {
                for(i in wordLength-1 downTo 0) {
                    if(word[i] == c) acc = i
                    this[i] = acc
                }
            }
        }

    private inline fun firstAfter(char: Char, position: Int) =
        if(position >= wordLength) wordLength else N[char - 'a'][position]

    private val D = Array(MAX_RELIGION_LENGTH+1) {
        Array(MAX_RELIGION_LENGTH+1) {
            IntArray(MAX_RELIGION_LENGTH+1)
        }
    }.also { it[0][0][0] = -1 }

    private inline fun updateD(n1: Int, n2: Int, n3: Int) {
        D[n1][n2][n3] = minOf(
            if(n1 > 0) firstAfter(religions[0][n1-1], D[n1-1][n2][n3] + 1) else wordLength,
            if(n2 > 0) firstAfter(religions[1][n2-1], D[n1][n2-1][n3] + 1) else wordLength,
            if(n3 > 0) firstAfter(religions[2][n3-1], D[n1][n2][n3-1] + 1) else wordLength
        )
    }

    fun append(religion: Int, char: Char) {
        val i = religion - 1
        religions[i][religionSizes[i]] = char
        religionSizes[i]++
        when(i) {
            0 -> {
                for(x in 0..religionSizes[1]) {
                    for(y in 0..religionSizes[2]) {
                        updateD(religionSizes[0], x, y)
                    }
                }
            }
            1 -> {
                for(x in 0..religionSizes[0]) {
                    for(y in 0..religionSizes[2]) {
                        updateD(x, religionSizes[1], y)
                    }
                }
            }
            2 -> {
                for(x in 0..religionSizes[0]) {
                    for(y in 0..religionSizes[1]) {
                        updateD(x, y, religionSizes[2])
                    }
                }
            }
        }
    }

    fun removeLast(religion: Int) {
        religionSizes[religion-1] --
    }

    val isGood: Boolean get() = D[religionSizes[0]][religionSizes[1]][religionSizes[2]] < wordLength
}

fun readLn() = readLine()!!
fun readInt() = readLn().toInt()
fun readLong() = readLn().toLong()
fun readStrings() = readLn().split(' ')
fun readInts() = readStrings().map { it.toInt() }
fun readDoubles() = readStrings().map { it.toDouble() }
fun readLongs() = readStrings().map { it.toLong() }

// helper for competitive programming; faster at printing many lines than repeated println calls
class Output {
    private val sb = StringBuilder()
    fun print(o: Any?) { sb.append(o) }
    fun println(o: Any?) { sb.append(o).append('\n') }
    fun println() { sb.append('\n') }
    @JvmName("_print") fun Any?.print() = print(this)
    @JvmName("_println") fun Any?.println() = println(this)
    fun nowPrint() { kotlin.io.print(sb) }
}
inline fun output(block: Output.()->Unit) { Output().apply(block).nowPrint() }


import java.io.OutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.Arrays;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.io.BufferedReader;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 *
 * @author __shangu__
 */
public class Main {
  public static void main(String[] args) {
    InputStream inputStream = System.in;
    OutputStream outputStream = System.out;
    InputReader in = new InputReader(inputStream);
    PrintWriter out = new PrintWriter(outputStream);
    DThreeReligions solver = new DThreeReligions();
    solver.solve(1, in, out);
    out.close();
  }

  static class DThreeReligions {
    public void solve(int testNumber, InputReader in, PrintWriter out) {
      int len = in.nextInt(), nop = in.nextInt();
      String ln = in.nextLine();
      int[] lastocc = new int[30];
      int[][] nextocc = new int[len][30];
      Arrays.fill(lastocc, -1);
      for (int i = len - 1; i >= 0; --i) {
        nextocc[i] = lastocc.clone();
        lastocc[ln.charAt(i) - 'a'] = i;
      }

      StringBuilder word[] = new StringBuilder[3];
      for (int i = 0; i < 3; ++i) word[i] = new StringBuilder();
      for (int i = 0; i < nop; ++i) {
        char op = in.next().charAt(0);
        int rel = in.nextInt() - 1;
        switch (op) {
          case '+':
            char ch = in.next().charAt(0);
            word[rel].append(ch);
            break;
          default:
            word[rel].deleteCharAt(word[rel].length() - 1);
        }

        boolean taken[] = new boolean[len], ok = true, tmp = false;
        int map[] = {0, 1, 2};
        Arrays.fill(taken, false);
        for (rel = 0; rel < 3 && ok; ++rel) {
          int u = 0;
          for (int pos = 0; pos < word[map[rel]].length() && ok; ++pos) {
            while (u != -1 && (taken[u] || ln.charAt(u) != word[map[rel]].charAt(pos))) {
              u = nextocc[u][word[map[rel]].charAt(pos) - 'a'];
            }
            if (u == -1) {
              ok = false;
              break;
            }
            taken[u] = true;
          }
        }
        tmp = tmp || ok;

        map = new int[]{0, 2, 1};
        ok = true;
        Arrays.fill(taken, false);
        for (rel = 0; rel < 3 && ok; ++rel) {
          int u = 0;
          for (int pos = 0; pos < word[map[rel]].length() && ok; ++pos) {
            while (u != -1 && (taken[u] || ln.charAt(u) != word[map[rel]].charAt(pos))) {
              u = nextocc[u][word[map[rel]].charAt(pos) - 'a'];
            }
            if (u == -1) {
              ok = false;
              break;
            }
            taken[u] = true;
          }
        }
        tmp = tmp || ok;

        map = new int[]{1, 0, 2};
        ok = true;
        Arrays.fill(taken, false);
        for (rel = 0; rel < 3 && ok; ++rel) {
          int u = 0;
          for (int pos = 0; pos < word[map[rel]].length() && ok; ++pos) {
            while (u != -1 && (taken[u] || ln.charAt(u) != word[map[rel]].charAt(pos))) {
              u = nextocc[u][word[map[rel]].charAt(pos) - 'a'];
            }
            if (u == -1) {
              ok = false;
              break;
            }
            taken[u] = true;
          }
        }
        tmp = tmp || ok;

        map = new int[]{1, 2, 0};
        ok = true;
        Arrays.fill(taken, false);
        for (rel = 0; rel < 3 && ok; ++rel) {
          int u = 0;
          for (int pos = 0; pos < word[map[rel]].length() && ok; ++pos) {
            while (u != -1 && (taken[u] || ln.charAt(u) != word[map[rel]].charAt(pos))) {
              u = nextocc[u][word[map[rel]].charAt(pos) - 'a'];
            }
            if (u == -1) {
              ok = false;
              break;
            }
            taken[u] = true;
          }
        }
        tmp = tmp || ok;
        map = new int[]{2, 0, 1};
        ok = true;
        Arrays.fill(taken, false);
        for (rel = 0; rel < 3 && ok; ++rel) {
          int u = 0;
          for (int pos = 0; pos < word[map[rel]].length() && ok; ++pos) {
            while (u != -1 && (taken[u] || ln.charAt(u) != word[map[rel]].charAt(pos))) {
              u = nextocc[u][word[map[rel]].charAt(pos) - 'a'];
            }
            if (u == -1) {
              ok = false;
              break;
            }
            taken[u] = true;
          }
        }
        tmp = tmp || ok;
        map = new int[]{2, 1, 0};
        ok = true;
        Arrays.fill(taken, false);
        for (rel = 0; rel < 3 && ok; ++rel) {
          int u = 0;
          for (int pos = 0; pos < word[map[rel]].length() && ok; ++pos) {
            while (u != -1 && (taken[u] || ln.charAt(u) != word[map[rel]].charAt(pos))) {
              u = nextocc[u][word[map[rel]].charAt(pos) - 'a'];
            }
            if (u == -1) {
              ok = false;
              break;
            }
            taken[u] = true;
          }
        }
        tmp = tmp || ok;
        out.println(tmp ? "YES" : "NO");
      }
    }

  }

  static class InputReader {
    public BufferedReader reader;
    public StringTokenizer tokenizer;

    public InputReader(InputStream stream) {
      reader = new BufferedReader(new InputStreamReader(stream), 32768);
      tokenizer = null;
    }

    public String next() {
      while (tokenizer == null || !tokenizer.hasMoreTokens()) {
        try {
          tokenizer = new StringTokenizer(reader.readLine());
        } catch (IOException e) {
          throw new RuntimeException(e);
        }
      }
      return tokenizer.nextToken();
    }

    public int nextInt() {
      return Integer.parseInt(next());
    }

    public String nextLine() {
      String str = "";
      try {
        str = reader.readLine();
      } catch (IOException e) {
        e.printStackTrace();
      }
      return str;
    }

  }
}