#define _CRT_SECURE_NO_WARNINGS
#pragma comment(linker, "/stack:256000000")
#include<utility>
#include<iostream>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<ctime>
#include<functional>
#include<random>
#include<climits>
#include<queue>
#include<stack>
#include<deque>
#include<bitset>
#include<map>
#include<set>
#include<unordered_map>
#include<unordered_set>
#include<cassert>
#include<ccomplex>

//4Head

#define mp make_pair
#define X first
#define Y second

using namespace std;
using ll = long long;
using ull = unsigned long long;
using ld = long double;
mt19937_64 gen(time(NULL));

ll const maxn = 3e5+5;
ll const maxx = 251;
ll nxt[maxn][26];
ll cur[26];
ll dp[251][251][251];
signed main() {
	ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	ll n, q; cin >> n >> q;
	string s; cin >> s;
	s = "#" + s;
	for (ll i = 0; i < 26; i++) {
		cur[i] = maxn;
	}
	for (ll i = 0; i < 26; i++) nxt[n][i] = maxn;
	cur[s[n] - 'a'] = n;
	for (ll i = n - 1; i >= 0; i--) {
		for (ll c = 0; c < 26; c++) {
			nxt[i][c] = cur[c];
		}
		if(i > 0) cur[s[i] - 'a'] = i;
	}
	vector<string>kek(4, "");
	for (ll i = 0; i < maxx; i++) {
		for (ll j = 0; j < maxx; j++) {
			for (ll k = 0; k < maxx; k++) {
				dp[i][j][k] = maxn;
			}
		}
	}
	dp[0][0][0] = 0;
	while (q--) {
		char c; cin >> c;
		ll t; cin >> t;
		if (c == '+') {
			char cc; cin >> cc;
			kek[t] += cc;
			//dp[kek[1].size()][kek[2].size()][kek[3].size()] = maxn;
			for (ll i = (t != 1 ? 0 : kek[1].size()); i <= kek[1].size(); i++) {
				for (ll j = (t != 2 ? 0 : kek[2].size()); j <= kek[2].size(); j++) {
					for (ll k = (t != 3 ? 0 : kek[3].size()); k <= kek[3].size(); k++) {
						dp[i][j][k] = maxn;
						if (i && dp[i - 1][j][k] != maxn) {
							dp[i][j][k] = min(dp[i][j][k], nxt[dp[i - 1][j][k]][kek[1][i-1] - 'a']);
						}
						if (j && dp[i][j - 1][k] != maxn) {
							dp[i][j][k] = min(dp[i][j][k], nxt[dp[i][j - 1][k]][kek[2][j-1] - 'a']);
						}
						if (k && dp[i][j][k - 1] != maxn) {
							dp[i][j][k] = min(dp[i][j][k], nxt[dp[i][j][k - 1]][kek[3][k-1] - 'a']);
						}
						ll tt = 0;
					}
				}
			}
		}
		else {
			kek[t].pop_back();
		}
		if (dp[kek[1].size()][kek[2].size()][kek[3].size()] == maxn) {
			cout << "NO\n";
		}
		else {
			cout << "YES\n";
		}
	}
	return 0;
}

/*

*/


#define _CRT_SECURE_NO_WARNINGS
#pragma comment(linker, "/STACK:64777216")

#include <unordered_map>
#include <unordered_set>
#include <set>
#include <map>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <queue>
#define speed ios_base::sync_with_stdio(0); cin tie(0); cout tie(0)
#define ll long long
#define eps 1e-8
#define mp(_FIRST,_SECOND) make_pair(_FIRST,_SECOND)
using namespace std;

inline char FastI() {
	char c = ' '; int a = 0;
	c = _getchar_nolock();
	while (!isdigit(c)) c = _getchar_nolock();
	while (isdigit(c)) {
		a *= 10;
		a += c - '0';
		c = _getchar_nolock();
	}
	return c;
}

inline void FastO(int val) {
	bool c = 0;
	if (val < 0)_putchar_nolock('-'), val *= -1;
	for (int i = 1000000000; i >= 1; i /= 10) {
		if (val / i) {
			c = 1;
			_putc_nolock(val / i + '0', stdout);
			val %= i;
		}
		else if (c) _putc_nolock('0', stdout);
	}
	if (!c) _putc_nolock('0', stdout);
}

const int MAXN = 1e5 + 10;
const int MAXM = 2e5 + 10;
const int INF = 1e9;
const long long MOD = 1e9 + 7;
const long long p = 37;

char ss[MAXN];
int nxt_letter[MAXN][30];
int dp[300][300][300];
int string_len[10];
char strings[10][300];

void dp_calc(int k)
{
	int st_1 = 0, st_2 = 0, st_3 = 0;
	if (k == 1)
		st_1 = string_len[1];
	if (k == 2)
		st_2 = string_len[2];
	if (k == 3)
		st_3 = string_len[3];
	for (int i = st_1; i <= string_len[1]; i++)
	{
		for (int j = st_2; j <= string_len[2]; j++)
		{
			for (int h = st_3; h <= string_len[3]; h++)
			{
				if (i == 0 && j == 0 && k == 0)
				{
					dp[i][j][k] = 0;
					continue;
				}
				dp[i][j][h] = INF;
				if (i > 0 && dp[i - 1][j][h] != INF)
					dp[i][j][h] = min(dp[i][j][h], nxt_letter[dp[i - 1][j][h]][strings[1][i] - 'a']);
				if (j > 0 && dp[i][j - 1][h] != INF)
					dp[i][j][h] = min(dp[i][j][h], nxt_letter[dp[i][j - 1][h]][strings[2][j] - 'a']);
				if (h > 0 && dp[i][j][h - 1] != INF)
					dp[i][j][h] = min(dp[i][j][h], nxt_letter[dp[i][j][h - 1]][strings[3][h] - 'a']);
			}
		}
	}
}

void solve()
{
	int n, q;
	scanf("%d %d", &n, &q);
	scanf("%s", ss);
	string s = ss;
	s = " " + s;
	for (int i = 0; i <= n + 1; i++)
	{
		for (int j = 0; j < 26; j++)
			nxt_letter[i][j] = INF;
	}
	for (int i = n; i >= 0; i--)
	{
		for (int j = 0; j < 26; j++)
			nxt_letter[i][j] = min(nxt_letter[i + 1][j], nxt_letter[i][j]);
		if (i > 0)
			nxt_letter[i - 1][s[i] - 'a'] = i;
	}
	for (int i = 0; i < q; i++)
	{
		scanf("\n");
		char t;
		scanf("%c", &t);
		if (t == '+')
		{
			int k;
			char c;
			scanf("%d %c", &k, &c);
			string_len[k]++;
			strings[k][string_len[k]] = c;
			dp_calc(k);
			if (dp[string_len[1]][string_len[2]][string_len[3]] != INF)
				printf("YES\n");
			else
				printf("NO\n");
		}
		else
		{
			int k;
			scanf("%d", &k);
			string_len[k]--;
			if (dp[string_len[1]][string_len[2]][string_len[3]] != INF)
				printf("YES\n");
			else
				printf("NO\n");
		}
	}
}

int main()
{
#ifdef LOCALFILE
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	solve();
	//multitest();
	//system("pause");
}


#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <algorithm>
#include <time.h>
//#include <math.h>
#include <cmath>
#include <fstream>
#include <deque>
#include <unordered_map>
#include <unordered_set>
#include <stdlib.h>
#include <iomanip>
#include <cstdio>
#include <cassert>

using namespace std;

#define flu fflush(stdout)
#define gg return 0
#define mp make_pair

long long mod = /*27449*//*998244353*//*1e9 + 7*/ 1e6 + 3;
long long inf = 1e18;
double eps = 1e-2;

ifstream in("input.txt");
ofstream out("output.txt");

int main()
{
	int n, q;
	cin >> n >> q;
	string s;
	cin >> s;
	vector<vector<int>> links(n + 3, vector<int>(26, n + 1));
	for (int i = n - 1; i >= 0; i--)
	{
		for (int j = 0; j < 26; j++)
		{
			links[i][j] = links[i + 1][j];
		}
		links[i][s[i] - 'a'] = i;
	}
	string s1, s2, s3;
	vector<vector<vector<int>>> dp(251, vector<vector<int>>(251, vector<int>(251, n + 2)));
	dp[0][0][0] = 0;
	int a = 0, b = 0, c = 0;
	for (int w = 0; w < q; w++)
	{
		char ch;
		cin >> ch;
		int t;
		cin >> t;
		if (ch == '+')
		{
			cin >> ch;
			if (t == 1)
			{
				s1.push_back(ch);
				a++;
				for (int i = 0; i <= b; i++)
				{
					for (int j = 0; j <= c; j++)
					{
						dp[a][i][j] = links[dp[a - 1][i][j]][ch - 'a'] + 1;
						if (i > 0) dp[a][i][j] = min(dp[a][i][j], links[dp[a][i - 1][j]][s2[i - 1] - 'a'] + 1);
						if (j > 0) dp[a][i][j] = min(dp[a][i][j], links[dp[a][i][j - 1]][s3[j - 1] - 'a'] + 1);
					}
				}
			}
			if (t == 2)
			{
				s2.push_back(ch);
				b++;
				for (int i = 0; i <= a; i++)
				{
					for (int j = 0; j <= c; j++)
					{
						dp[i][b][j] = links[dp[i][b - 1][j]][ch - 'a'] + 1;
						if (i > 0) dp[i][b][j] = min(dp[i][b][j], links[dp[i - 1][b][j]][s1[i - 1] - 'a'] + 1);
						if (j > 0) dp[i][b][j] = min(dp[i][b][j], links[dp[i][b][j - 1]][s3[j - 1] - 'a'] + 1);
					}
				}
			}
			if (t == 3)
			{
				s3.push_back(ch);
				c++;
				for (int i = 0; i <= a; i++)
				{
					for (int j = 0; j <= b; j++)
					{
						dp[i][j][c] = links[dp[i][j][c - 1]][ch - 'a'] + 1;
						if (i > 0) dp[i][j][c] = min(dp[i][j][c], links[dp[i - 1][j][c]][s1[i - 1] - 'a'] + 1);
						if (j > 0) dp[i][j][c] = min(dp[i][j][c], links[dp[i][j - 1][c]][s2[j - 1] - 'a'] + 1);
					}
				}
			}
		}
		else
		{
			if (t == 1)
			{
				for (int i = 0; i <= b; i++)
				{
					for (int j = 0; j <= c; j++)
					{
						dp[a][i][j] = n + 2;
					}
				}
				s1.pop_back();
				a--;
			}
			if (t == 2)
			{
				for (int i = 0; i <= a; i++)
				{
					for (int j = 0; j <= c; j++)
					{
						dp[i][b][j] = n + 2;
					}
				}
				s2.pop_back();
				b--;
			}
			if (t == 3)
			{
				for (int i = 0; i <= a; i++)
				{
					for (int j = 0; j <= b; j++)
					{
						dp[i][j][c] = n + 2;
					}
				}
				s3.pop_back();
				c--;
			}
		}
		if (dp[a][b][c] != n + 2)
		{
			cout << "YES" << endl;
		}
		else
		{
			cout << "NO" << endl;
		}
	}
	gg;
}


#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <algorithm>
#include <time.h>
//#include <math.h>
#include <cmath>
#include <fstream>
#include <deque>
#include <unordered_map>
#include <unordered_set>
#include <stdlib.h>
#include <iomanip>
#include <cstdio>
#include <cassert>

using namespace std;

#define flu fflush(stdout)
#define gg return 0
#define mp make_pair

long long mod = /*27449*//*998244353*//*1e9 + 7*/ 1e6 + 3;
long long inf = 1e18;
double eps = 1e-2;

ifstream in("input.txt");
ofstream out("output.txt");

int main()
{
	int n, q;
	cin >> n >> q;
	string s;
	cin >> s;
	vector<vector<int>> links(n + 3, vector<int>(26, n + 1));
	for (int i = n - 1; i >= 0; i--)
	{
		for (int j = 0; j < 26; j++)
		{
			links[i][j] = links[i + 1][j];
		}
		links[i][s[i] - 'a'] = i;
	}
	string s1, s2, s3;
	vector<vector<vector<int>>> dp(251, vector<vector<int>>(251, vector<int>(251, n + 2)));
	dp[0][0][0] = 0;
	for (int w = 0; w < q; w++)
	{
		char ch;
		cin >> ch;
		int t;
		cin >> t;
		if (ch == '+')
		{
			cin >> ch;
			if (t == 1)
			{
				s1.push_back(ch);
				int c = s1.size();
				int m = s2.size();
				int k = s3.size();
				for (int i = 0; i <= m; i++)
				{
					for (int j = 0; j <= k; j++)
					{
						dp[c][i][j] = links[dp[c - 1][i][j]][ch - 'a'] + 1;
						if (i > 0) dp[c][i][j] = min(dp[c][i][j], links[dp[c][i - 1][j]][s2.back() - 'a'] + 1);
						if (j > 0) dp[c][i][j] = min(dp[c][i][j], links[dp[c][i][j - 1]][s3.back() - 'a'] + 1);
					}
				}
			}
			if (t == 2)
			{
				s2.push_back(ch);
				int c = s2.size();
				int m = s1.size();
				int k = s3.size();
				for (int i = 0; i <= m; i++)
				{
					for (int j = 0; j <= k; j++)
					{
						dp[i][c][j] = links[dp[i][c - 1][j]][ch - 'a'] + 1;
						if (i > 0) dp[i][c][j] = min(dp[i][c][j], links[dp[i - 1][c][j]][s1.back() - 'a'] + 1);
						if (j > 0) dp[i][c][j] = min(dp[i][c][j], links[dp[i][c][j - 1]][s3.back() - 'a'] + 1);
					}
				}
			}
			if (t == 3)
			{
				s3.push_back(ch);
				int c = s3.size();
				int m = s1.size();
				int k = s2.size();
				for (int i = 0; i <= m; i++)
				{
					for (int j = 0; j <= k; j++)
					{
						dp[i][j][c] = links[dp[i][j][c - 1]][ch - 'a'] + 1;
						if (i > 0) dp[i][j][c] = min(dp[i][j][c], links[dp[i - 1][j][c]][s1.back() - 'a'] + 1);
						if (j > 0) dp[i][j][c] = min(dp[i][j][c], links[dp[i][j - 1][c]][s2.back() - 'a'] + 1);
					}
				}
			}
		}
		else
		{
			if (t == 1)
			{
				for (int i = 0; i < s2.size(); i++)
				{
					for (int j = 0; j < s3.size(); j++)
					{
						dp[s1.size()][i][j] = n + 2;
					}
				}
				s1.pop_back();
			}
			if (t == 2)
			{
				for (int i = 0; i < s1.size(); i++)
				{
					for (int j = 0; j < s3.size(); j++)
					{
						dp[i][s2.size()][j] = n + 2;
					}
				}
				s2.pop_back();
			}
			if (t == 3)
			{
				for (int i = 0; i < s1.size(); i++)
				{
					for (int j = 0; j < s2.size(); j++)
					{
						dp[i][j][s3.size()] = n + 2;
					}
				}
				s3.pop_back();
			}
		}
		if (dp[s1.size()][s2.size()][s3.size()] != n + 2)
		{
			cout << "YES" << endl;
		}
		else
		{
			cout << "NO" << endl;
		}
	}
	gg;
}


#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <algorithm>
#include <time.h>
//#include <math.h>
#include <cmath>
#include <fstream>
#include <deque>
#include <unordered_map>
#include <unordered_set>
#include <stdlib.h>
#include <iomanip>
#include <cstdio>
#include <cassert>

using namespace std;

#define flu fflush(stdout)
#define gg return 0
#define mp make_pair

long long mod = /*27449*//*998244353*//*1e9 + 7*/ 1e6 + 3;
long long inf = 1e18;
double eps = 1e-2;

ifstream in("input.txt");
ofstream out("output.txt");

int main()
{
	int n, q;
	cin >> n >> q;
	string s;
	cin >> s;
	vector<vector<int>> links(n + 3, vector<int>(26, n + 1));
	for (int i = n - 1; i >= 0; i--)
	{
		for (int j = 0; j < 26; j++)
		{
			links[i][j] = links[i + 1][j];
		}
		links[i][s[i] - 'a'] = i;
	}
	string s1, s2, s3;
	vector<vector<vector<int>>> dp(251, vector<vector<int>>(251, vector<int>(251, n + 2)));
	dp[0][0][0] = 0;
	int a = 0, b = 0, c = 0;
	for (int w = 0; w < q; w++)
	{
		char ch;
		cin >> ch;
		int t;
		cin >> t;
		if (ch == '+')
		{
			cin >> ch;
			if (t == 1)
			{
				s1.push_back(ch);
				a++;
				for (int i = 0; i <= b; i++)
				{
					for (int j = 0; j <= c; j++)
					{
						dp[a][i][j] = links[dp[a - 1][i][j]][ch - 'a'] + 1;
						if (i > 0) dp[a][i][j] = min(dp[a][i][j], links[dp[a][i - 1][j]][s2[i - 1] - 'a'] + 1);
						if (j > 0) dp[a][i][j] = min(dp[a][i][j], links[dp[a][i][j - 1]][s3[j - 1] - 'a'] + 1);
					}
				}
			}
			if (t == 2)
			{
				s2.push_back(ch);
				b++;
				for (int i = 0; i <= a; i++)
				{
					for (int j = 0; j <= c; j++)
					{
						dp[i][b][j] = links[dp[i][b - 1][j]][ch - 'a'] + 1;
						if (i > 0) dp[i][b][j] = min(dp[i][b][j], links[dp[i - 1][b][j]][s1[i - 1] - 'a'] + 1);
						if (j > 0) dp[i][b][j] = min(dp[i][b][j], links[dp[i][b][j - 1]][s3[j - 1] - 'a'] + 1);
					}
				}
			}
			if (t == 3)
			{
				s3.push_back(ch);
				c++;
				for (int i = 0; i <= a; i++)
				{
					for (int j = 0; j <= c; j++)
					{
						dp[i][j][c] = links[dp[i][j][c - 1]][ch - 'a'] + 1;
						if (i > 0) dp[i][j][c] = min(dp[i][j][c], links[dp[i - 1][j][c]][s1[i - 1] - 'a'] + 1);
						if (j > 0) dp[i][j][c] = min(dp[i][j][c], links[dp[i][j - 1][c]][s2[j - 1] - 'a'] + 1);
					}
				}
			}
		}
		else
		{
			if (t == 1)
			{
				for (int i = 0; i <= b; i++)
				{
					for (int j = 0; j <= c; j++)
					{
						dp[a][i][j] = n + 2;
					}
				}
				s1.pop_back();
				a--;
			}
			if (t == 2)
			{
				for (int i = 0; i <= a; i++)
				{
					for (int j = 0; j <= c; j++)
					{
						dp[i][b][j] = n + 2;
					}
				}
				s2.pop_back();
				b--;
			}
			if (t == 3)
			{
				for (int i = 0; i <= a; i++)
				{
					for (int j = 0; j <= b; j++)
					{
						dp[i][j][c] = n + 2;
					}
				}
				s3.pop_back();
				c--;
			}
		}
		if (dp[a][b][c] != n + 2)
		{
			cout << "YES" << endl;
		}
		else
		{
			cout << "NO" << endl;
		}
	}
	gg;
}


#include <iostream>
#include <vector>
#include <string>

#define INF 1<<30
#define N 251
#define L 100010
#define A 26
using namespace std;

string wou;
int n,q;
int nx[L][A], p[A];
int dp[N][N][N], len[4], pre[4];
char des[4][L];

int main()
{
	cin>>n>>q>>wou;
	wou=" "+wou;
	for(int i=0;i<26;i++)
	{
		nx[n+1][i]=n+1;
		p[i]=n+1;
	}
	for(int i=n;i>=0;i--)
	{
		for(int j=0;j<26;j++) nx[i][j]=p[j];
		if(i) p[wou[i]-'a'] = i;
	}

	while(q--)
	{
		string type,c;
		int x;
		cin>>type>>x;

		for(int i=1;i<4;i++) pre[i]=0;
		if(type[0]=='-')
		{
			len[x]--;
			cout<< (dp[len[1]][len[2]][len[3]]<=n?"YES":"NO")<<"\n";
			continue;
		}
		cin>>c;
		pre[x] = len[x];
		des[x][len[x]++] = c[0];

		for(int i=pre[1];i<=len[1];i++) for(int j=pre[2];j<=len[2];j++) for(int k=pre[3];k<=len[3];k++)
		{
			if(i==0 && j==0 && k==0)continue;
			dp[i][j][k] = INF;
			if(i) dp[i][j][k]= min(dp[i][j][k], nx[ dp[i-1][j][k] ][ des[1][i-1]-'a' ]);
			if(j) dp[i][j][k]= min(dp[i][j][k], nx[ dp[i][j-1][k] ][ des[2][j-1]-'a' ]);
			if(k) dp[i][j][k]= min(dp[i][j][k], nx[ dp[i][j][k-1] ][ des[3][k-1]-'a' ]);
		}
		cout<< (dp[len[1]][len[2]][len[3]]<=n?"YES":"NO")<<"\n";
	}
}


#include <iostream>
#include <vector>
#include <string>

#define INF 1<<30
#define N 251
#define L 100010
#define A 26
using namespace std;

string wou;
int n,q;
int nx[L][A], p[A];
int dp[N][N][N], len[4], pre[4];
char des[4][L];

int main()
{
	cin>>n>>q>>wou;
	wou=" "+wou;
	for(int i=0;i<26;i++)
	{
		nx[n+1][i]=n+1;
		p[i]=n+1;
	}
	for(int i=n;i>=0;i--)
	{
		for(int j=0;j<26;j++) nx[i][j]=p[j];
		if(i) p[wou[i]-'a'] = i;
	}

	while(q--)
	{
		string type,c;
		int x;
		cin>>type>>x;

		for(int i=1;i<4;i++) pre[i]=0;
		if(type[0]=='-')
		{
			len[x]--;
			cout<< (dp[len[1]][len[2]][len[3]]<=n?"YES":"NO")<<"\n";
			continue;
		}
		cin>>c;
		pre[x] = len[x];
		des[x][len[x]++] = c[0];

		for(int i=pre[1];i<=len[1];i++) for(int j=pre[2];j<=len[2];j++) for(int k=pre[3];k<=len[3];k++)
		{
			if(i==0 && j==0 && k==0)continue;
			dp[i][j][k] = INF;
			if(i) dp[i][j][k]= min(dp[i][j][k], nx[ dp[i-1][j][k] ][ des[1][i-1]-'a' ]);
			if(j) dp[i][j][k]= min(dp[i][j][k], nx[ dp[i][j-1][k] ][ des[2][j-1]-'a' ]);
			if(k) dp[i][j][k]= min(dp[i][j][k], nx[ dp[i][j][k-1] ][ des[3][k-1]-'a' ]);
        }
        cout<< (dp[len[1]][len[2]][len[3]]<=n?"YES":"NO")<<"\n";
	}
}


#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>

char* erastosthen_sieve(int n);

/*********************************************************
 *                      List<int>
 *********************************************************/

typedef struct list_node_int list_node_int;
struct list_node_int {
    int value;
    list_node_int* prev;
    list_node_int* next;
};

typedef struct {
    int size;
    list_node_int* head;
    list_node_int* tail;
} list_int;

list_int* list_int_create();
list_node_int* list_int_add(list_int* list, int value);
void list_int_remove(list_int* list, list_node_int* node);
void list_int_delete(list_int* list);

/*********************************************************
 *                      Set<int>
 *********************************************************/
typedef struct {
    int size;
    int table_size;
    list_int** table;
} set_int;

set_int* set_int_create(int capacity);
void set_int_add(set_int* set, int value);
int set_int_contains(set_int* set, int value);
int set_int_remove(set_int* set, int value);
void set_int_delete(set_int* set);

/*********************************************************
 *                      List<Entry<int,int>>
 *********************************************************/

typedef struct {
    int key;
    int value;
} entry_int;
typedef struct list_node_entry_int list_node_entry_int;
struct list_node_entry_int {
    entry_int* entry;
    list_node_entry_int* prev;
    list_node_entry_int* next;
};
typedef struct {
    int size;
    list_node_entry_int* head;
    list_node_entry_int* tail;
} list_entry_int;

list_entry_int* list_entry_int_create();
list_node_entry_int* list_entry_int_add(list_entry_int* list, int key, int value);
void list_entry_int_remove(list_entry_int* list, list_node_entry_int* node);
void list_entry_int_delete(list_entry_int* list);

/*********************************************************
 *                      Map<Entry<int,int>>
 *********************************************************/
typedef struct {
    int size;
    int table_size;
    list_entry_int** table;
} map_entry_int;

map_entry_int* map_entry_int_create(int capacity);
void map_entry_int_put(map_entry_int* map, int key, int value);
int map_entry_int_get(map_entry_int* map, int key);
int map_entry_int_contains_key(map_entry_int* map, int key);
int map_entry_int_remove(map_entry_int* map, int key);
void map_entry_int_delete(map_entry_int* map);

/*********************************************************
 *                      MAIN
 *********************************************************/
#define COUNT 256
int main() {
    int n, q;
    scanf("%d %d\n", &n, &q);

    char* word = malloc((n + 1) * sizeof(char));
    scanf("%s\n", word);

    int letters[COUNT];
    memset(letters, 0, COUNT * sizeof(int));

    int limits[COUNT];
    memset(limits, 0, COUNT * sizeof(int));
    for (int i = 0; i < n; ++i) {
        limits[word[i]]++;
    }

    int len[3];
    len[0] = 0;
    len[1] = 0;
    len[2] = 0;

    char* relig[3];
    int str_bytes = (q + 1) * sizeof(char);
    for (int i = 0; i < 3; ++i) {
        relig[i] = malloc(str_bytes);
        memset(relig[i], 0, str_bytes);
    }

    set_int* conflicts = set_int_create(COUNT * 2);

    for (int change = 0; change < q; ++change) {
        char sign;
        int i;
        scanf("%c %d", &sign, &i);

        int length = len[i - 1];
        char* a = relig[i - 1];
        char changed_char;
        if (sign == '+') {
            scanf(" %c", &changed_char);
            letters[changed_char]++;
            a[length] = changed_char;
            a[length + 1] = 0;
            len[i - 1] = length + 1;
        } else if (sign == '-') {
            changed_char = a[length - 1];
            letters[changed_char]--;
            a[length - 1] = 0;
            len[i - 1] = length - 1;
        } else {
            return 1;
        }
        scanf("\n");

        if (letters[changed_char] > limits[changed_char]) {
            set_int_add(conflicts, changed_char);
        } else {
            set_int_remove(conflicts, changed_char);
        }

        if (conflicts->size == 0) {
            printf("YES\n");
        } else {
            printf("NO\n");
        }
    }

    return 0;
}

char* erastosthen_sieve(int n) {
    char* a = malloc((n + 1) * sizeof(char));
    if (a == NULL) {
        return NULL;
    }
    a[0] = 0;
    a[1] = 0;
    for (int i = 2; i < n + 1; ++i) {
        a[i] = 1;
    }
    int max_sqrt = sqrt(n) + 1;
    for (int i = 2; i < max_sqrt; ++i) {
        if (a[i]) {
            for (int j = i; j < (n + 1) / i; ++j) {
                a[i*j] = 0;
            }
        }
    }
    return a;
}

/*********************************************************
 *                      List<int>
 *********************************************************/

list_int* list_int_create() {
    list_int* list = malloc(sizeof(list_int));
    list->size = 0;
    list->head = NULL;
    list->tail = NULL;
    return list;
}

list_node_int* list_int_add(list_int* list, int value) {
    list_node_int* node = malloc(sizeof(list_node_int));
    node->value = value;
    node->prev = list->tail;
    node->next = NULL;
    if (!list->head) {
        list->head = node;
    }
    if (list->tail) {
        list->tail->next = node;
    }
    list->tail = node;
    list->size++;
    return node;
}

void list_int_remove(list_int* list, list_node_int* node) {
    if (node->prev) {
        node->prev->next = node->next;
    }
    if (list->head == node) {
        list->head = node->next;
    }
    if (node->next) {
        node->next->prev = node->prev;
    }
    if (list->tail == node) {
        list->tail = node->prev;
    }
    free(node);
    list->size--;
}

void list_int_delete(list_int* list) {
    list_node_int* node = list->head;
    while (node) {
        list_node_int* next = node->next;
        free(node);
        node = next;
    }
    free(list);
}

void test_list_int() {
    list_int* list = list_int_create();
    list_int_add(list, 1);
    list_int_add(list, 2);
    list_node_int* node3 = list_int_add(list, 3);
    list_int_add(list, 4);
    list_int_add(list, 5);
    list_int_remove(list, list->tail);
    list_int_add(list, 6);
    list_int_remove(list, list->head);
    list_int_add(list, 7);
    list_int_remove(list, node3);
    list_int_add(list, 8);
    list_int_add(list, 9);
    list_int_add(list, 10);

    list_node_int* node = list->head;
    while (node) {
        printf("%d\n", node->value);
        node = node->next;
    }
    printf("size=%d\n", list->size);
}

/*********************************************************
 *                      Set<int>
 *********************************************************/
set_int* set_int_create(int capacity) {
    set_int* set = malloc(sizeof(set_int));
    set->size = 0;
    int byte_count = capacity * sizeof(list_int*);
    set->table = malloc(byte_count);
    memset(set->table, 0, byte_count);
    set->table_size = capacity;
    return set;
}

void set_int_resize(set_int* set, int capacity) {
    int byte_count = capacity * sizeof(list_int*);
    list_int** new_table = malloc(byte_count);
    memset(new_table, 0, byte_count);
    for (int i = 0; i < set->table_size; ++i) {
        if (set->table[i]) {
            list_int* bucket = set->table[i];
            list_node_int* node = bucket->head;
            while (node) {
                int hash = node->value % capacity;
                list_int* new_bucket = new_table[hash];
                if (!new_bucket) {
                    new_bucket = list_int_create();
                    new_table[hash] = new_bucket;
                }
                list_int_add(new_bucket, node->value);
                node = node->next;
            }
            list_int_delete(bucket);
        }
    }
    free(set->table);
    set->table = new_table;
    set->table_size = capacity;
}

void set_int_add(set_int* set, int value) {
    double load_factor = (double) set->size / set->table_size;
    if (load_factor >= 0.75) {
        set_int_resize(set, set->table_size * 2);
    }

    int hash = value % set->table_size;
    list_int** table = set->table;
    list_int* bucket = table[hash];
    if (!bucket) {
        bucket = list_int_create();
        table[hash] = bucket;
    }
    list_node_int* node = bucket->head;
    while (node) {
        if (node->value == value) {
            return;
        }
        node = node->next;
    }
    list_int_add(bucket, value);
    set->size++;
}

int set_int_contains(set_int* set, int value) {
    int hash = value % set->table_size;
    list_int** table = set->table;
    list_int* bucket = table[hash];
    if (!bucket) {
        return 0;
    }
    list_node_int* node = bucket->head;
    while (node) {
        if (node->value == value) {
            return 1;
        }
        node = node->next;
    }
    return 0;
}

int set_int_remove(set_int* set, int value) {
    int hash = value % set->table_size;
    list_int** table = set->table;
    list_int* bucket = table[hash];
    if (!bucket) {
        return 0;
    }
    list_node_int* node = bucket->head;
    while (node) {
        if (node->value == value) {
            list_int_remove(bucket, node);
            set->size--;
            return 1;
        }
        node = node->next;
    }
    return 0;
}

void set_int_delete(set_int* set) {
    for (int i = 0; i < set->table_size; ++i) {
        if (set->table[i]) {
            list_int_delete(set->table[i]);
        }
    }
    free(set->table);
    free(set);
}

void test_set_int() {
    set_int* set = set_int_create(1);
    set_int_add(set, 1);
    set_int_add(set, 2);
    set_int_add(set, 3);
    set_int_add(set, 4);
    set_int_add(set, 5);
    set_int_add(set, 6);
    set_int_add(set, 7);
    set_int_add(set, 8);
    set_int_add(set, 9);
    set_int_remove(set, 4);
    printf("size=%d\n", set->size);
    printf("set[0]=%d\n", set_int_contains(set, 0));
    printf("set[1]=%d\n", set_int_contains(set, 1));
    printf("set[2]=%d\n", set_int_contains(set, 2));
    printf("set[3]=%d\n", set_int_contains(set, 3));
    printf("set[4]=%d\n", set_int_contains(set, 4));
    printf("set[5]=%d\n", set_int_contains(set, 5));
    printf("set[9]=%d\n", set_int_contains(set, 9));
    printf("set[10]=%d\n", set_int_contains(set, 10));
    set_int_delete(set);
}

/*********************************************************
 *                      List<Entry<int,int>>
 *********************************************************/

list_entry_int* list_entry_int_create() {
    list_entry_int* list = malloc(sizeof(list_entry_int));
    list->size = 0;
    list->head = NULL;
    list->tail = NULL;
    return list;
}

list_node_entry_int* list_entry_int_add(list_entry_int* list, int key, int value) {
    list_node_entry_int* node = malloc(sizeof(list_node_entry_int));
    entry_int* entry = malloc(sizeof(entry_int));
    entry->key = key;
    entry->value = value;
    node->entry = entry;
    node->prev = list->tail;
    node->next = NULL;
    if (!list->head) {
        list->head = node;
    }
    if (list->tail) {
        list->tail->next = node;
    }
    list->tail = node;
    list->size++;
    return node;
}

void list_entry_int_remove(list_entry_int* list, list_node_entry_int* node) {
    if (node->prev) {
        node->prev->next = node->next;
    }
    if (list->head == node) {
        list->head = node->next;
    }
    if (node->next) {
        node->next->prev = node->prev;
    }
    if (list->tail == node) {
        list->tail = node->prev;
    }
    free(node->entry);
    free(node);
    list->size--;
}

void list_entry_int_delete(list_entry_int* list) {
    list_node_entry_int* node = list->head;
    while (node) {
        list_node_entry_int* next = node->next;
        free(node->entry);
        free(node);
        node = next;
    }
    free(list);
}

/*********************************************************
 *                      Map<Entry<int,int>>
 *********************************************************/
map_entry_int* map_entry_int_create(int capacity) {
    map_entry_int* map = malloc(sizeof(map_entry_int));
    map->size = 0;
    int byte_count = capacity * sizeof(list_entry_int*);
    map->table = malloc(byte_count);
    memset(map->table, 0, byte_count);
    map->table_size = capacity;
    return map;
}

void map_entry_int_resize(map_entry_int* map, int capacity) {
    int byte_count = capacity * sizeof(list_entry_int*);
    list_entry_int** new_table = malloc(byte_count);
    memset(new_table, 0, byte_count);
    for (int i = 0; i < map->table_size; ++i) {
        if (map->table[i]) {
            list_entry_int* bucket = map->table[i];
            list_node_entry_int* node = bucket->head;
            while (node) {
                int hash = node->entry->key % capacity;
                list_entry_int* new_bucket = new_table[hash];
                if (!new_bucket) {
                    new_bucket = list_entry_int_create();
                    new_table[hash] = new_bucket;
                }
                list_entry_int_add(new_bucket, node->entry->key, node->entry->value);
                node = node->next;
            }
            list_entry_int_delete(bucket);
        }
    }
    free(map->table);
    map->table = new_table;
    map->table_size = capacity;
}

void map_entry_int_put(map_entry_int* map, int key, int value) {
    double load_factor = (double) map->size / map->table_size;
    if (load_factor >= 0.75) {
        map_entry_int_resize(map, map->table_size * 2);
    }

    int hash = key % map->table_size;
    list_entry_int** table = map->table;
    list_entry_int* bucket = table[hash];
    if (!bucket) {
        bucket = list_entry_int_create();
        table[hash] = bucket;
    }
    list_node_entry_int* node = bucket->head;
    while (node) {
        if (node->entry->key == key) {
            node->entry->value = value;
            return;
        }
        node = node->next;
    }
    list_entry_int_add(bucket, key, value);
    map->size++;
}

int map_entry_int_get(map_entry_int* map, int key) {
    int hash = key % map->table_size;
    list_entry_int** table = map->table;
    list_entry_int* bucket = table[hash];
    if (!bucket) {
        return 0;
    }
    list_node_entry_int* node = bucket->head;
    while (node) {
        if (node->entry->key == key) {
            return node->entry->value;
        }
        node = node->next;
    }
    return 0;
}

int map_entry_int_contains_key(map_entry_int* map, int key) {
    int hash = key % map->table_size;
    list_entry_int** table = map->table;
    list_entry_int* bucket = table[hash];
    if (!bucket) {
        return 0;
    }
    list_node_entry_int* node = bucket->head;
    while (node) {
        if (node->entry->key == key) {
            return 1;
        }
        node = node->next;
    }
    return 0;
}

int map_entry_int_remove(map_entry_int* map, int key) {
    int hash = key % map->table_size;
    list_entry_int** table = map->table;
    list_entry_int* bucket = table[hash];
    if (!bucket) {
        return 0;
    }
    list_node_entry_int* node = bucket->head;
    while (node) {
        if (node->entry->key == key) {
            list_entry_int_remove(bucket, node);
            map->size--;
            return 1;
        }
        node = node->next;
    }
    return 0;
}

void map_entry_int_delete(map_entry_int* map) {
    for (int i = 0; i < map->table_size; ++i) {
        if (map->table[i]) {
            list_entry_int_delete(map->table[i]);
        }
    }
    free(map->table);
    free(map);
}

void test_map_entry_int() {
    map_entry_int* map = map_entry_int_create(1);
    map_entry_int_put(map, 1, 1);
    map_entry_int_put(map, 2, 2);
    map_entry_int_put(map, 3, 3);
    map_entry_int_put(map, 4, 4);
    map_entry_int_put(map, 5, 5);
    map_entry_int_put(map, 6, 6);
    map_entry_int_put(map, 7, 7);
    map_entry_int_put(map, 8, 8);
    map_entry_int_put(map, 9, 9);
    map_entry_int_remove(map, 4);
    printf("size=%d\n", map->size);
    printf("map[0]=%d\n", map_entry_int_get(map, 0));
    printf("map[1]=%d\n", map_entry_int_get(map, 1));
    printf("map[2]=%d\n", map_entry_int_get(map, 2));
    printf("map[3]=%d\n", map_entry_int_get(map, 3));
    printf("map[4]=%d\n", map_entry_int_get(map, 4));
    printf("map[5]=%d\n", map_entry_int_get(map, 5));
    printf("map[9]=%d\n", map_entry_int_get(map, 9));
    printf("map[10]=%d\n", map_entry_int_get(map, 10));
    map_entry_int_delete(map);
}


#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>

int min2(int a, int b);
int min3(int a, int b, int c);
char* erastosthen_sieve(int n);

/*********************************************************
 *                      List<int>
 *********************************************************/

typedef struct list_node_int list_node_int;
struct list_node_int {
    int value;
    list_node_int* prev;
    list_node_int* next;
};

typedef struct {
    int size;
    list_node_int* head;
    list_node_int* tail;
} list_int;

list_int* list_int_create();
list_node_int* list_int_add(list_int* list, int value);
void list_int_remove(list_int* list, list_node_int* node);
void list_int_delete(list_int* list);

/*********************************************************
 *                      Set<int>
 *********************************************************/
typedef struct {
    int size;
    int table_size;
    list_int** table;
} set_int;

set_int* set_int_create(int capacity);
void set_int_add(set_int* set, int value);
int set_int_contains(set_int* set, int value);
int set_int_remove(set_int* set, int value);
void set_int_delete(set_int* set);

/*********************************************************
 *                      List<Entry<int,int>>
 *********************************************************/

typedef struct {
    int key;
    int value;
} entry_int;
typedef struct list_node_entry_int list_node_entry_int;
struct list_node_entry_int {
    entry_int* entry;
    list_node_entry_int* prev;
    list_node_entry_int* next;
};
typedef struct {
    int size;
    list_node_entry_int* head;
    list_node_entry_int* tail;
} list_entry_int;

list_entry_int* list_entry_int_create();
list_node_entry_int* list_entry_int_add(list_entry_int* list, int key, int value);
void list_entry_int_remove(list_entry_int* list, list_node_entry_int* node);
void list_entry_int_delete(list_entry_int* list);

/*********************************************************
 *                      Map<Entry<int,int>>
 *********************************************************/
typedef struct {
    int size;
    int table_size;
    list_entry_int** table;
} map_entry_int;

map_entry_int* map_entry_int_create(int capacity);
void map_entry_int_put(map_entry_int* map, int key, int value);
int map_entry_int_get(map_entry_int* map, int key);
int map_entry_int_contains_key(map_entry_int* map, int key);
int map_entry_int_remove(map_entry_int* map, int key);
void map_entry_int_delete(map_entry_int* map);

/*********************************************************
 *                      MAIN
 *********************************************************/
#define MAX 251
int main() {
    int n, q;
    scanf("%d %d\n", &n, &q);

    char* word = malloc((n + 1) * sizeof(char));
    scanf("%s\n", word);

    int** N = malloc(26 * sizeof(int*));
    int* N_mem = malloc(26 * (n + 2) * sizeof(int));
    for (int c = 0; c < 26; ++c) {
        N[c] = N_mem + c * (n + 2);
    }
    for (int c = 0; c < 26; ++c) {
        N[c][n+1] = n;
        N[c][n  ] = n;
        for (int i = n - 1; i >= 0; --i) {
            if (word[i] == 'a' + c) {
                N[c][i] = i;
            } else {
                N[c][i] = N[c][i+1];
            }
        }
    }

    /*for (int c = 0; c < 4; ++c) {
        printf("%c: ", 'a' + c);
        for (int i = 0; i < n + 2; ++i) {
            printf("%d ", N[c][i]);
        }
        printf("\n");
    }*/

    int*** D = malloc(MAX * sizeof(int**));
    int** p_bytes_1 = malloc(MAX * MAX * sizeof(int*));
    int* p_bytes_2 = malloc(MAX * MAX * MAX * sizeof(int));
    for (int i = 0; i < MAX; ++i) {
        D[i] = p_bytes_1 + i * MAX;
        for (int j = 0; j < MAX; ++j) {
            D[i][j] = p_bytes_2 + i * MAX * MAX + j * MAX;
        }
    }

    D[0][0][0] = -1;

    int len[3];
    char* religion[3];
    for (int i = 0; i < 3; ++i) {
        int n_bytes = (q + 1) * sizeof(char);
        len[i] = 0;
        religion[i] = malloc(n_bytes);
        memset(religion[i], 0, n_bytes);
    }

    //printf("%s\n", word);
    for (int change = 0; change < q; ++change) {
        char sign;
        int i;
        scanf("%c %d", &sign, &i);

        int length = len[i - 1];
        char* a = religion[i - 1];
        if (sign == '+') {
            char new_char;
            scanf(" %c", &new_char);
            a[length] = new_char;
            a[length + 1] = 0;
            len[i - 1] = length + 1;

            int min1 = i == 1 ? len[0] : 0;
            int min2 = i == 2 ? len[1] : 0;
            int min3 = i == 3 ? len[2] : 0;
            for (int n1 = min1; n1 <= len[0]; n1++) {
                for (int n2 = min2; n2 <= len[1]; n2++) {
                    for (int n3 = min3; n3 <= len[2]; n3++) {
                        int* min = &D[n1][n2][n3];
                        *min = n;
                        int d = n;
                        if (n1) {
                            d = N[religion[0][n1-1] - 'a'][D[n1-1][n2][n3] + 1];
                            *min = d < *min ? d : *min;
                        }
                        if (n2) {
                            d = N[religion[1][n2-1] - 'a'][D[n1][n2-1][n3] + 1];
                            *min = d < *min ? d : *min;
                        }
                        if (n3) {
                            d = N[religion[2][n3-1] - 'a'][D[n1][n2][n3-1] + 1];
                            *min = d < *min ? d : *min;
                        }
                    }
                }
            }
        } else if (sign == '-') {
            a[length - 1] = 0;
            len[i - 1] = length - 1;
        } else {
            return 1;
        }
        scanf("\n");

        int d = D[len[0]][len[1]][len[2]];
        if (d < n) {
            //printf("%c %d %c YES D[%d,%d,%d]=%d\n", sign, i, changed_char, len[0], len[1], len[2], d);
            printf("YES\n");
        } else {
            //printf("%c %d %c NO D[%d,%d,%d]=%d\n", sign, i, changed_char, len[0], len[1], len[2], d);
            printf("NO\n");
        }
    }

    return 0;
}

/*********************************************************
 *                      Functions
 *********************************************************/

int min2(int a, int b) {
    if (a <= b) {
        return a;
    } else {
        return b;
    }
}

int min3(int a, int b, int c) {
    int min = a;
    min = min2(min, b);
    min = min2(min, c);
    return min;
}

char* erastosthen_sieve(int n) {
    char* a = malloc((n + 1) * sizeof(char));
    if (a == NULL) {
        return NULL;
    }
    a[0] = 0;
    a[1] = 0;
    for (int i = 2; i < n + 1; ++i) {
        a[i] = 1;
    }
    int max_sqrt = sqrt(n) + 1;
    for (int i = 2; i < max_sqrt; ++i) {
        if (a[i]) {
            for (int j = i; j < (n + 1) / i; ++j) {
                a[i*j] = 0;
            }
        }
    }
    return a;
}

/*********************************************************
 *                      List<int>
 *********************************************************/

list_int* list_int_create() {
    list_int* list = malloc(sizeof(list_int));
    list->size = 0;
    list->head = NULL;
    list->tail = NULL;
    return list;
}

list_node_int* list_int_add(list_int* list, int value) {
    list_node_int* node = malloc(sizeof(list_node_int));
    node->value = value;
    node->prev = list->tail;
    node->next = NULL;
    if (!list->head) {
        list->head = node;
    }
    if (list->tail) {
        list->tail->next = node;
    }
    list->tail = node;
    list->size++;
    return node;
}

void list_int_remove(list_int* list, list_node_int* node) {
    if (node->prev) {
        node->prev->next = node->next;
    }
    if (list->head == node) {
        list->head = node->next;
    }
    if (node->next) {
        node->next->prev = node->prev;
    }
    if (list->tail == node) {
        list->tail = node->prev;
    }
    free(node);
    list->size--;
}

void list_int_delete(list_int* list) {
    list_node_int* node = list->head;
    while (node) {
        list_node_int* next = node->next;
        free(node);
        node = next;
    }
    free(list);
}

void test_list_int() {
    list_int* list = list_int_create();
    list_int_add(list, 1);
    list_int_add(list, 2);
    list_node_int* node3 = list_int_add(list, 3);
    list_int_add(list, 4);
    list_int_add(list, 5);
    list_int_remove(list, list->tail);
    list_int_add(list, 6);
    list_int_remove(list, list->head);
    list_int_add(list, 7);
    list_int_remove(list, node3);
    list_int_add(list, 8);
    list_int_add(list, 9);
    list_int_add(list, 10);

    list_node_int* node = list->head;
    while (node) {
        printf("%d\n", node->value);
        node = node->next;
    }
    printf("size=%d\n", list->size);
}

/*********************************************************
 *                      Set<int>
 *********************************************************/
set_int* set_int_create(int capacity) {
    set_int* set = malloc(sizeof(set_int));
    set->size = 0;
    int byte_count = capacity * sizeof(list_int*);
    set->table = malloc(byte_count);
    memset(set->table, 0, byte_count);
    set->table_size = capacity;
    return set;
}

void set_int_resize(set_int* set, int capacity) {
    int byte_count = capacity * sizeof(list_int*);
    list_int** new_table = malloc(byte_count);
    memset(new_table, 0, byte_count);
    for (int i = 0; i < set->table_size; ++i) {
        if (set->table[i]) {
            list_int* bucket = set->table[i];
            list_node_int* node = bucket->head;
            while (node) {
                int hash = node->value % capacity;
                list_int* new_bucket = new_table[hash];
                if (!new_bucket) {
                    new_bucket = list_int_create();
                    new_table[hash] = new_bucket;
                }
                list_int_add(new_bucket, node->value);
                node = node->next;
            }
            list_int_delete(bucket);
        }
    }
    free(set->table);
    set->table = new_table;
    set->table_size = capacity;
}

void set_int_add(set_int* set, int value) {
    double load_factor = (double) set->size / set->table_size;
    if (load_factor >= 0.75) {
        set_int_resize(set, set->table_size * 2);
    }

    int hash = value % set->table_size;
    list_int** table = set->table;
    list_int* bucket = table[hash];
    if (!bucket) {
        bucket = list_int_create();
        table[hash] = bucket;
    }
    list_node_int* node = bucket->head;
    while (node) {
        if (node->value == value) {
            return;
        }
        node = node->next;
    }
    list_int_add(bucket, value);
    set->size++;
}

int set_int_contains(set_int* set, int value) {
    int hash = value % set->table_size;
    list_int** table = set->table;
    list_int* bucket = table[hash];
    if (!bucket) {
        return 0;
    }
    list_node_int* node = bucket->head;
    while (node) {
        if (node->value == value) {
            return 1;
        }
        node = node->next;
    }
    return 0;
}

int set_int_remove(set_int* set, int value) {
    int hash = value % set->table_size;
    list_int** table = set->table;
    list_int* bucket = table[hash];
    if (!bucket) {
        return 0;
    }
    list_node_int* node = bucket->head;
    while (node) {
        if (node->value == value) {
            list_int_remove(bucket, node);
            set->size--;
            return 1;
        }
        node = node->next;
    }
    return 0;
}

void set_int_delete(set_int* set) {
    for (int i = 0; i < set->table_size; ++i) {
        if (set->table[i]) {
            list_int_delete(set->table[i]);
        }
    }
    free(set->table);
    free(set);
}

void test_set_int() {
    set_int* set = set_int_create(1);
    set_int_add(set, 1);
    set_int_add(set, 2);
    set_int_add(set, 3);
    set_int_add(set, 4);
    set_int_add(set, 5);
    set_int_add(set, 6);
    set_int_add(set, 7);
    set_int_add(set, 8);
    set_int_add(set, 9);
    set_int_remove(set, 4);
    printf("size=%d\n", set->size);
    printf("set[0]=%d\n", set_int_contains(set, 0));
    printf("set[1]=%d\n", set_int_contains(set, 1));
    printf("set[2]=%d\n", set_int_contains(set, 2));
    printf("set[3]=%d\n", set_int_contains(set, 3));
    printf("set[4]=%d\n", set_int_contains(set, 4));
    printf("set[5]=%d\n", set_int_contains(set, 5));
    printf("set[9]=%d\n", set_int_contains(set, 9));
    printf("set[10]=%d\n", set_int_contains(set, 10));
    set_int_delete(set);
}

/*********************************************************
 *                      List<Entry<int,int>>
 *********************************************************/

list_entry_int* list_entry_int_create() {
    list_entry_int* list = malloc(sizeof(list_entry_int));
    list->size = 0;
    list->head = NULL;
    list->tail = NULL;
    return list;
}

list_node_entry_int* list_entry_int_add(list_entry_int* list, int key, int value) {
    list_node_entry_int* node = malloc(sizeof(list_node_entry_int));
    entry_int* entry = malloc(sizeof(entry_int));
    entry->key = key;
    entry->value = value;
    node->entry = entry;
    node->prev = list->tail;
    node->next = NULL;
    if (!list->head) {
        list->head = node;
    }
    if (list->tail) {
        list->tail->next = node;
    }
    list->tail = node;
    list->size++;
    return node;
}

void list_entry_int_remove(list_entry_int* list, list_node_entry_int* node) {
    if (node->prev) {
        node->prev->next = node->next;
    }
    if (list->head == node) {
        list->head = node->next;
    }
    if (node->next) {
        node->next->prev = node->prev;
    }
    if (list->tail == node) {
        list->tail = node->prev;
    }
    free(node->entry);
    free(node);
    list->size--;
}

void list_entry_int_delete(list_entry_int* list) {
    list_node_entry_int* node = list->head;
    while (node) {
        list_node_entry_int* next = node->next;
        free(node->entry);
        free(node);
        node = next;
    }
    free(list);
}

/*********************************************************
 *                      Map<Entry<int,int>>
 *********************************************************/
map_entry_int* map_entry_int_create(int capacity) {
    map_entry_int* map = malloc(sizeof(map_entry_int));
    map->size = 0;
    int byte_count = capacity * sizeof(list_entry_int*);
    map->table = malloc(byte_count);
    memset(map->table, 0, byte_count);
    map->table_size = capacity;
    return map;
}

void map_entry_int_resize(map_entry_int* map, int capacity) {
    int byte_count = capacity * sizeof(list_entry_int*);
    list_entry_int** new_table = malloc(byte_count);
    memset(new_table, 0, byte_count);
    for (int i = 0; i < map->table_size; ++i) {
        if (map->table[i]) {
            list_entry_int* bucket = map->table[i];
            list_node_entry_int* node = bucket->head;
            while (node) {
                int hash = node->entry->key % capacity;
                list_entry_int* new_bucket = new_table[hash];
                if (!new_bucket) {
                    new_bucket = list_entry_int_create();
                    new_table[hash] = new_bucket;
                }
                list_entry_int_add(new_bucket, node->entry->key, node->entry->value);
                node = node->next;
            }
            list_entry_int_delete(bucket);
        }
    }
    free(map->table);
    map->table = new_table;
    map->table_size = capacity;
}

void map_entry_int_put(map_entry_int* map, int key, int value) {
    double load_factor = (double) map->size / map->table_size;
    if (load_factor >= 0.75) {
        map_entry_int_resize(map, map->table_size * 2);
    }

    int hash = key % map->table_size;
    list_entry_int** table = map->table;
    list_entry_int* bucket = table[hash];
    if (!bucket) {
        bucket = list_entry_int_create();
        table[hash] = bucket;
    }
    list_node_entry_int* node = bucket->head;
    while (node) {
        if (node->entry->key == key) {
            node->entry->value = value;
            return;
        }
        node = node->next;
    }
    list_entry_int_add(bucket, key, value);
    map->size++;
}

int map_entry_int_get(map_entry_int* map, int key) {
    int hash = key % map->table_size;
    list_entry_int** table = map->table;
    list_entry_int* bucket = table[hash];
    if (!bucket) {
        return 0;
    }
    list_node_entry_int* node = bucket->head;
    while (node) {
        if (node->entry->key == key) {
            return node->entry->value;
        }
        node = node->next;
    }
    return 0;
}

int map_entry_int_contains_key(map_entry_int* map, int key) {
    int hash = key % map->table_size;
    list_entry_int** table = map->table;
    list_entry_int* bucket = table[hash];
    if (!bucket) {
        return 0;
    }
    list_node_entry_int* node = bucket->head;
    while (node) {
        if (node->entry->key == key) {
            return 1;
        }
        node = node->next;
    }
    return 0;
}

int map_entry_int_remove(map_entry_int* map, int key) {
    int hash = key % map->table_size;
    list_entry_int** table = map->table;
    list_entry_int* bucket = table[hash];
    if (!bucket) {
        return 0;
    }
    list_node_entry_int* node = bucket->head;
    while (node) {
        if (node->entry->key == key) {
            list_entry_int_remove(bucket, node);
            map->size--;
            return 1;
        }
        node = node->next;
    }
    return 0;
}

void map_entry_int_delete(map_entry_int* map) {
    for (int i = 0; i < map->table_size; ++i) {
        if (map->table[i]) {
            list_entry_int_delete(map->table[i]);
        }
    }
    free(map->table);
    free(map);
}

void test_map_entry_int() {
    map_entry_int* map = map_entry_int_create(1);
    map_entry_int_put(map, 1, 1);
    map_entry_int_put(map, 2, 2);
    map_entry_int_put(map, 3, 3);
    map_entry_int_put(map, 4, 4);
    map_entry_int_put(map, 5, 5);
    map_entry_int_put(map, 6, 6);
    map_entry_int_put(map, 7, 7);
    map_entry_int_put(map, 8, 8);
    map_entry_int_put(map, 9, 9);
    map_entry_int_remove(map, 4);
    printf("size=%d\n", map->size);
    printf("map[0]=%d\n", map_entry_int_get(map, 0));
    printf("map[1]=%d\n", map_entry_int_get(map, 1));
    printf("map[2]=%d\n", map_entry_int_get(map, 2));
    printf("map[3]=%d\n", map_entry_int_get(map, 3));
    printf("map[4]=%d\n", map_entry_int_get(map, 4));
    printf("map[5]=%d\n", map_entry_int_get(map, 5));
    printf("map[9]=%d\n", map_entry_int_get(map, 9));
    printf("map[10]=%d\n", map_entry_int_get(map, 10));
    map_entry_int_delete(map);
}


#include <stdio.h>
#include <string.h>
#include <ctype.h>
#ifndef LL
#define LL long long
#endif
#ifndef uLL
#define uLL unsigned long long
#endif
#ifndef uL
#define uL unsigned int
#endif
#ifndef iL
#define iL int
#endif
#ifndef iS
#define iS short
#endif
#ifndef uS
#define uS unsigned short
#endif
#ifndef uC
#define uC unsigned char
#endif
#define max(a,b) (((a)>(b))?(a):(b))
#define min(a,b) (((a)<(b))?(a):(b))
#define abs(a) (((a)<0)?(-(a)):(a))
#define sgn(a) (((a)<0)?(1):(0))
#define movs(a,b,l) (memmove((a),(b),sizeof(*(b))*(l)))
#define stosb(a,b,l) (memset((a),(b),sizeof(*(a))*(l)))
char str[200000];
LL jmp[200000][26];
LL sc[]={1,300,90000};
LL dp[300][300][300];
char str2[3][300];
int main()
{
	//freopen("in.in","r",stdin);
	char ch;
	LL a,b,c,i,j,k,n,m,q,ans1,ans2,ans3,s[3],iq,t1,t2;
	LL *cur;
	scanf("%lld%lld\n",&n,&m);
	for (i=1;i<=n;i++)
	{
		RE:scanf("%c",&str[i]);
		if (isalpha(str[i])==0) {goto RE;}
		str[i]=tolower(str[i])-'a';
	}
	for (j=0;j<26;j++) {jmp[n+1][j]=(LL)0xFFFFFFFF;}
	for (i=n;i>=1;i--)
	{
		for (j=0;j<26;j++)
		{
			if (str[i]==j)
			{
				jmp[i][j]=i;
			}
			else
			{
				jmp[i][j]=jmp[i+1][j];
			}
		}
	}
	for (j=0;j<26;j++) {jmp[0][j]=jmp[1][j];}
	//printf("%lld*\n",jmp[0][0]);
	s[0]=0;s[1]=0;s[2]=0;
	dp[0][0][0]=0;
	//printf("%lld\n",jmp[0][0]);
	for (iq=1;iq<=m;iq++)
	{
		RE2:scanf("%c",&ch);
		//printf("%lld %c--\n",iq,ch);
		if (ch=='+')
		{
			scanf("%lld",&a);a-=1;
			RE3:scanf("%c",&ch);
			if (isalpha(ch)==0) {goto RE3;}
			b=tolower(ch)-'a';
			s[a]+=1;str2[a][s[a]]=b;
			t1=(a+1)%3;t2=(a+2)%3;
			//printf("shit %lld %lld %lld %lld %lld\n",a,b,s[t1],s[t2],s[a]);
			for (i=0;i<=s[t1];i++)
			{
				for (j=0;j<=s[t2];j++)
				{
					//printf("%lld %lld+\n",i,j);
					ans1=ans2=ans3=(LL)0xFFFFFFFF;
					if (i>0)
					{
						cur=&dp[0][0][0]+(i-1)*sc[t1]+j*sc[t2]+s[a]*sc[a];
						if (*cur<ans1)
						{
							ans1=jmp[*cur+1][str2[t1][i]];
						}
					}
					//printf("%lld %lld\n",i,j);
					if (j>0)
					{
						cur=&dp[0][0][0]+i*sc[t1]+(j-1)*sc[t2]+s[a]*sc[a];
						if (*cur<ans2)
						{
							ans2=jmp[*cur+1][str2[t2][j]];
						}
					}
					//printf("%lld %lld\n",i,j);
					cur=&dp[0][0][0]+i*sc[t1]+j*sc[t2]+(s[a]-1)*sc[a];
					//printf("%lld %lld %lld %lld\n",i,j,cur-&dp[0][0][0],*cur);
					if (*cur<ans3)
					{
						ans3=jmp[*cur+1][b];
					}
					*(&dp[0][0][0]+i*sc[t1]+j*sc[t2]+s[a]*sc[a])=min(min(ans1,ans2),ans3);
					//printf("%lld %lld-\n",i,j);
				}
			}
			//printf("%lld-\n",dp[s[2]][s[1]][s[0]]);
		}
		else if (ch=='-')
		{
			scanf("%lld",&a);a-=1;
			s[a]-=1;
			//printf("%lld-\n",dp[s[2]][s[1]][s[0]]);
		}
		else {goto RE2;}
		if (dp[s[2]][s[1]][s[0]]<=n)
		{
			printf("YES\n");
		}
		else
		{
			printf("NO\n");
		}
	}
	return 0;
}


using System;
using System.Collections.Generic;
using System.Linq;
using CompLib.Util;

public class Program
{
    int N, Q;
    char[] S;
    const int MaxWordLen = 250;
    public void Solve()
    {
        var sc = new Scanner();
        N = sc.NextInt();
        Q = sc.NextInt();
        S = sc.NextCharArray();
        // q個クエリ
        // 1,2,3に + cを追加 - ラスト削除
        // sの各文字に重ならない部分文字列1,2,3が作れるか?
        int[] last = new int[26];
        for (int i = 0; i < 26; i++)
        {
            last[i] = N + 1;
        }
        int[,] next = new int[N, 26];
        for (int i = N - 1; i >= 0; i--)
        {

            last[S[i] - 'a'] = i + 1;
            for (int j = 0; j < 26; j++)
            {
                next[i, j] = last[j];
            }
        }

        // Sの何もじめまで 1,2,3のi文字目まで作る
        int[,,] dp = new int[MaxWordLen + 1, MaxWordLen + 1, MaxWordLen + 1];

        for (int i = 0; i <= MaxWordLen; i++)
        {
            for (int j = 0; j <= MaxWordLen; j++)
            {
                for (int k = 0; k <= MaxWordLen; k++)
                {
                    dp[i, j, k] = int.MaxValue;
                }
            }
        }

        dp[0, 0, 0] = 0;

        List<char>[] word = new List<char>[3];
        for (int i = 0; i < 3; i++)
        {
            word[i] = new List<char>(MaxWordLen);
        }

        Console.SetOut(new System.IO.StreamWriter(Console.OpenStandardOutput()) { AutoFlush = false });
        for (int w = 0; w < Q; w++)
        {
            char t = sc.NextChar();
            if (t == '+')
            {
                int idx = sc.NextInt() - 1;
                char c = sc.NextChar();
                int iMin = idx == 0 ? word[0].Count : 0;
                int iMax = idx == 0 ? word[0].Count + 1 : word[0].Count;

                int jMin = idx == 1 ? word[1].Count : 0;
                int jMax = idx == 1 ? word[1].Count + 1 : word[1].Count;

                int kMin = idx == 2 ? word[2].Count : 0;
                int kMax = idx == 2 ? word[2].Count + 1 : word[2].Count;

                word[idx].Add(c);
                for (int i = iMin; i <= iMax; i++)
                {
                    for (int j = jMin; j <= jMax; j++)
                    {
                        for (int k = kMin; k <= kMax; k++)
                        {
                            int cur = dp[i, j, k];
                            if (cur >= N) continue;
                            if (i + 1 <= iMax)
                            {
                                dp[i + 1, j, k] = Math.Min(dp[i + 1, j, k], next[cur, word[0][i] - 'a']);
                            }

                            if (j + 1 <= jMax)
                            {
                                dp[i, j + 1, k] = Math.Min(dp[i, j + 1, k], next[cur, word[1][j] - 'a']);
                            }

                            if (k + 1 <= kMax)
                            {
                                dp[i, j, k + 1] = Math.Min(dp[i, j, k + 1], next[cur, word[2][k] - 'a']);
                            }
                        }
                    }
                }

            }
            else
            {
                int idx = sc.NextInt() - 1;
                int iMin = idx == 0 ? word[0].Count : 0;
                int iMax = word[0].Count;

                int jMin = idx == 1 ? word[1].Count : 0;
                int jMax = word[1].Count;

                int kMin = idx == 2 ? word[2].Count : 0;
                int kMax = word[2].Count;

                word[idx].RemoveAt(word[idx].Count - 1);
                for (int i = iMin; i <= iMax; i++)
                {
                    for (int j = jMin; j <= jMax; j++)
                    {
                        for (int k = kMin; k <= kMax; k++)
                        {
                            dp[i, j, k] = int.MaxValue;
                        }
                    }
                }
            }

            // Console.WriteLine(dp[word[0].Count, word[1].Count, word[2].Count]);
            Console.WriteLine(dp[word[0].Count, word[1].Count, word[2].Count] <= N ? "YES" : "NO");
        }
        Console.Out.Flush();
    }

    public static void Main(string[] args) => new Program().Solve();
    // public static void Main(string[] args) => new Thread(new Program().Solve, 1 << 27).Start();
}

namespace CompLib.Util
{
    using System;
    using System.Linq;

    class Scanner
    {
        private string[] _line;
        private int _index;
        private const char Separator = ' ';

        public Scanner()
        {
            _line = new string[0];
            _index = 0;
        }

        public string Next()
        {
            if (_index >= _line.Length)
            {
                string s;
                do
                {
                    s = Console.ReadLine();
                } while (s.Length == 0);

                _line = s.Split(Separator);
                _index = 0;
            }

            return _line[_index++];
        }

        public string ReadLine()
        {
            _index = _line.Length;
            return Console.ReadLine();
        }

        public int NextInt() => int.Parse(Next());
        public long NextLong() => long.Parse(Next());
        public double NextDouble() => double.Parse(Next());
        public decimal NextDecimal() => decimal.Parse(Next());
        public char NextChar() => Next()[0];
        public char[] NextCharArray() => Next().ToCharArray();

        public string[] Array()
        {
            string s = Console.ReadLine();
            _line = s.Length == 0 ? new string[0] : s.Split(Separator);
            _index = _line.Length;
            return _line;
        }

        public int[] IntArray() => Array().Select(int.Parse).ToArray();
        public long[] LongArray() => Array().Select(long.Parse).ToArray();
        public double[] DoubleArray() => Array().Select(double.Parse).ToArray();
        public decimal[] DecimalArray() => Array().Select(decimal.Parse).ToArray();
    }
}


//! ----------------------------------------------
//! Framework <https://github.com/vain0x/procon>
//!
//! See the bottom of file for solution.
//! ----------------------------------------------

#![allow(unused_imports)]
#![allow(non_snake_case)]

use std::cell::RefCell;
use std::cmp::{max, min, Ordering};
use std::collections::*;
use std::fmt::{Debug, Display, Formatter, Write as FmtWrite};
use std::io::{stderr, stdin, BufRead, Write};
use std::mem::{replace, swap};
use std::ops::*;
use std::rc::Rc;

/// Print values to standard error if debug mode.
#[allow(unused_macros)]
macro_rules! debug {
    ($($e:expr),*) => {
        #[cfg(debug_assertions)]
        $({
            let (e, mut err) = (stringify!($e), stderr());
            writeln!(err, "\x1B[33m{}\x1B[0m = {:?}", e, $e).unwrap()
        })*
    };
}

/// Read from standard input and parse each word.
/// - `read!(T, U, ..)` parses a line as a tuple of words.
/// - `read![[T]]` parses a line as an array of words.
/// - `read![..; N]` parses `N` lines, using `read!(..)` repeatedly.
#[allow(unused_macros)]
macro_rules! read {
    ([$t:ty] ; $n:expr) =>
        ((0..$n).map(|_| read!([$t])).collect::<Vec<_>>());
    ($($t:ty),+ ; $n:expr) =>
        ((0..$n).map(|_| read!($($t),+)).collect::<Vec<_>>());
    ([$t:ty]) =>
        (rl().split_whitespace().map(|w| w.parse().unwrap()).collect::<Vec<$t>>());
    ($($t:ty),*) => {{
        let buf = rl();
        let mut w = buf.split_whitespace();
        ($(w.next().unwrap().parse::<$t>().unwrap()),*)
    }};
}

/// Read a line from standard input.
#[allow(dead_code)]
fn rl() -> String {
    let mut buf = String::new();
    stdin().read_line(&mut buf).unwrap();

    #[allow(deprecated)]
    buf.trim_right().to_owned()
}

// -----------------------------------------------
// Solution
// -----------------------------------------------

struct Solver {
    N: usize,
    Q: usize,
    S: Vec<char>,
    T: Vec<Vec<char>>,
}

impl Solver {
    fn push(&mut self, x: usize, c: char) {
        self.T[x].push(c);
    }

    fn pop(&mut self, x: usize) {
        self.T[x].pop();
    }

    fn ok(&self) -> bool {
        let K = (0..3).map(|x| self.T[x].len()).sum::<usize>();

        for perm in &[
            [0, 1, 2],
            [0, 2, 1],
            [1, 0, 2],
            [1, 2, 0],
            [2, 0, 1],
            [2, 1, 0],
        ] {
            let mut last = [0; 3];
            let mut k = 0;

            for i in 0..self.N {
                for &x in perm {
                    if let Some(&c) = self.T[x].get(last[x]) {
                        if self.S[i] == c {
                            last[x] += 1;
                            k += 1;
                            break;
                        }
                    }
                }

                if k == K {
                    return true;
                }
            }
        }

        false
    }

    fn solve(mut self) {
        for _ in 0..self.Q {
            let s = rl();
            let mut s = s.split_whitespace();
            let add = s.next().unwrap() == "+";
            let x = s.next().unwrap().parse::<usize>().unwrap() - 1;
            let c = if add {
                s.next().unwrap().chars().next().unwrap()
            } else {
                'x'
            };

            if add {
                self.push(x, c)
            } else {
                self.pop(x)
            }

            println!("{}", if self.ok() { "YES" } else { "NO" })
        }
    }
}

fn main() {
    let (N, Q) = read!(usize, usize);
    let S = rl().chars().collect::<Vec<_>>();

    Solver {
        N: N,
        Q: Q,
        S: S,
        T: vec![Vec::with_capacity(250); 3],
    }
    .solve()
}


//! ----------------------------------------------
//! Framework <https://github.com/vain0x/procon>
//!
//! See the bottom of file for solution.
//! ----------------------------------------------

#![allow(unused_imports)]
#![allow(non_snake_case)]

use std::cell::RefCell;
use std::cmp::{max, min, Ordering};
use std::collections::*;
use std::fmt::{Debug, Display, Formatter, Write as FmtWrite};
use std::io::{stderr, stdin, BufRead, Write};
use std::mem::{replace, swap};
use std::ops::*;
use std::rc::Rc;

/// Print values to standard error if debug mode.
#[allow(unused_macros)]
macro_rules! debug {
    ($($e:expr),*) => {
        #[cfg(debug_assertions)]
        $({
            let (e, mut err) = (stringify!($e), stderr());
            writeln!(err, "\x1B[33m{}\x1B[0m = {:?}", e, $e).unwrap()
        })*
    };
}

/// Read from standard input and parse each word.
/// - `read!(T, U, ..)` parses a line as a tuple of words.
/// - `read![[T]]` parses a line as an array of words.
/// - `read![..; N]` parses `N` lines, using `read!(..)` repeatedly.
#[allow(unused_macros)]
macro_rules! read {
    ([$t:ty] ; $n:expr) =>
        ((0..$n).map(|_| read!([$t])).collect::<Vec<_>>());
    ($($t:ty),+ ; $n:expr) =>
        ((0..$n).map(|_| read!($($t),+)).collect::<Vec<_>>());
    ([$t:ty]) =>
        (rl().split_whitespace().map(|w| w.parse().unwrap()).collect::<Vec<$t>>());
    ($($t:ty),*) => {{
        let buf = rl();
        let mut w = buf.split_whitespace();
        ($(w.next().unwrap().parse::<$t>().unwrap()),*)
    }};
}

/// Read a line from standard input.
#[allow(dead_code)]
fn rl() -> String {
    let mut buf = String::new();
    stdin().read_line(&mut buf).unwrap();

    #[allow(deprecated)]
    buf.trim_right().to_owned()
}

// -----------------------------------------------
// Solution
// -----------------------------------------------

struct Solver {
    N: usize,
    Q: usize,
    S: Vec<char>,
    T: Vec<Vec<char>>,

    // occ[c][i] = j : S[i..] において文字 c の最初の出現が S[j]
    occ: BTreeMap<char, Vec<usize>>,

    // dp[x][y][z] = k:
    // 宗教 0, 1, 2 の文字列のそれぞれ前方 x, y, z を使って S の 0..k を埋められる
    // ただし埋められないときは k > N とする
    dp: Vec<Vec<Vec<usize>>>,
}

impl Solver {
    #[allow(unused)]
    fn reset_dp(&mut self) {
        for x in 0..self.dp.len() {
            for y in 0..self.dp[x].len() {
                for z in 0..self.dp[x][y].len() {
                    self.dp[x][y][z] = self.N + 1;
                }
            }
        }
        self.dp[0][0][0] = 0;
    }

    // S[..k] を消費済みの状態で、宗教 h の x 文字目を割り当てるとき、消費する文字列の最短の長さ
    fn next(&self, k: usize, h: usize, x: usize) -> usize {
        if x >= self.T[h].len() {
            return self.N + 1;
        }
        self.occ[&self.T[h][x]][k] + 1
    }

    fn update(&mut self, x: usize, y: usize, z: usize) {
        let k = self.dp[x][y][z];
        let kx = self.next(k, 0, x);
        let ky = self.next(k, 1, y);
        let kz = self.next(k, 2, z);
        self.dp[x + 1][y][z] = min(self.dp[x + 1][y][z], kx);
        self.dp[x][y + 1][z] = min(self.dp[x][y + 1][z], ky);
        self.dp[x][y][z + 1] = min(self.dp[x][y][z + 1], kz);
    }

    #[allow(unused)]
    fn update_all(&mut self) {
        for x in 0..self.T[0].len() + 1 {
            for y in 0..self.T[1].len() + 1 {
                for z in 0..self.T[2].len() + 1 {
                    self.update(x, y, z);
                }
            }
        }
    }

    fn update_range(&mut self, g: usize, h: usize) -> Range<usize> {
        if g == h {
            self.T[h].len() - min(1, self.T[h].len())..self.T[h].len() + 1
        } else {
            0..self.T[g].len() + 1
        }
    }

    fn erase_range(&mut self, g: usize, h: usize) -> Range<usize> {
        if g == h {
            self.T[h].len()..self.T[h].len() + 1
        } else {
            0..self.T[g].len() + 1
        }
    }

    fn push(&mut self, h: usize, c: char) {
        self.T[h].push(c);

        // self.reset_dp();
        // self.update_all();
        for x in self.update_range(0, h) {
            for y in self.update_range(1, h) {
                for z in self.update_range(2, h) {
                    self.update(x, y, z);
                }
            }
        }
    }

    fn pop(&mut self, h: usize) {
        for x in self.erase_range(0, h) {
            for y in self.erase_range(1, h) {
                for z in self.erase_range(2, h) {
                    self.dp[x][y][z] = self.N + 1;
                }
            }
        }

        self.T[h].pop();

        // self.reset_dp();
        // self.update_all();

        for x in self.update_range(0, h) {
            for y in self.update_range(1, h) {
                for z in self.update_range(2, h) {
                    self.update(x, y, z);
                }
            }
        }
    }

    fn ok(&mut self) -> bool {
        let x = self.T[0].len();
        let y = self.T[1].len();
        let z = self.T[2].len();
        self.dp[x][y][z] <= self.N
    }

    fn solve(mut self) {
        for c in b'a'..b'z' + 1 {
            let c = c as char;
            let mut next = self.N;
            let mut occ = vec![self.N; self.N + 2];
            for i in (0..self.N).rev() {
                if self.S[i] == c {
                    next = i;
                }
                occ[i] = next;
            }
            self.occ.insert(c, occ);
        }

        // self.reset_dp();
        self.dp[0][0][0] = 0;

        for _ in 0..self.Q {
            let s = rl();
            let mut s = s.split_whitespace();
            let add = s.next().unwrap() == "+";
            let h = s.next().unwrap().parse::<usize>().unwrap() - 1;
            let c = if add {
                s.next().unwrap().chars().next().unwrap()
            } else {
                'x'
            };

            if add {
                self.push(h, c)
            } else {
                self.pop(h)
            }

            // for x in 0..3 {
            //     for y in 0..3 {
            //         for z in 0..3 {
            //             debug!(((x, y, z), self.dp[x][y][z]));
            //         }
            //     }
            // }

            println!("{}", if self.ok() { "YES" } else { "NO" })
        }
    }
}

fn main() {
    let (N, Q) = read!(usize, usize);
    let S = rl().chars().collect::<Vec<_>>();

    Solver {
        N: N,
        Q: Q,
        S: S,
        T: vec![Vec::with_capacity(250); 3],
        occ: BTreeMap::new(),
        dp: vec![vec![vec![N + 1; 251]; 251]; 251],
    }
    .solve()
}


//! ----------------------------------------------
//! Framework <https://github.com/vain0x/procon>
//!
//! See the bottom of file for solution.
//! ----------------------------------------------

#![allow(unused_imports)]
#![allow(non_snake_case)]

use std::cell::RefCell;
use std::cmp::{max, min, Ordering};
use std::collections::*;
use std::fmt::{Debug, Display, Formatter, Write as FmtWrite};
use std::io::{stderr, stdin, BufRead, Write};
use std::mem::{replace, swap};
use std::ops::*;
use std::rc::Rc;

/// Print values to standard error if debug mode.
#[allow(unused_macros)]
macro_rules! debug {
    ($($e:expr),*) => {
        #[cfg(debug_assertions)]
        $({
            let (e, mut err) = (stringify!($e), stderr());
            writeln!(err, "\x1B[33m{}\x1B[0m = {:?}", e, $e).unwrap()
        })*
    };
}

/// Read from standard input and parse each word.
/// - `read!(T, U, ..)` parses a line as a tuple of words.
/// - `read![[T]]` parses a line as an array of words.
/// - `read![..; N]` parses `N` lines, using `read!(..)` repeatedly.
#[allow(unused_macros)]
macro_rules! read {
    ([$t:ty] ; $n:expr) =>
        ((0..$n).map(|_| read!([$t])).collect::<Vec<_>>());
    ($($t:ty),+ ; $n:expr) =>
        ((0..$n).map(|_| read!($($t),+)).collect::<Vec<_>>());
    ([$t:ty]) =>
        (rl().split_whitespace().map(|w| w.parse().unwrap()).collect::<Vec<$t>>());
    ($($t:ty),*) => {{
        let buf = rl();
        let mut w = buf.split_whitespace();
        ($(w.next().unwrap().parse::<$t>().unwrap()),*)
    }};
}

/// Read a line from standard input.
#[allow(dead_code)]
fn rl() -> String {
    let mut buf = String::new();
    stdin().read_line(&mut buf).unwrap();

    #[allow(deprecated)]
    buf.trim_right().to_owned()
}

// -----------------------------------------------
// Solution
// -----------------------------------------------

struct Solver {
    N: usize,
    Q: usize,
    S: Vec<char>,
    T: Vec<Vec<char>>,

    // occ[c][i] = j : S[i..] において文字 c の最初の出現が S[j]
    occ: BTreeMap<char, Vec<usize>>,

    // dp[x][y][z] = k:
    // 宗教 0, 1, 2 の文字列のそれぞれ前方 x, y, z を使って S の 0..k を埋められる
    // ただし埋められないときは k > N とする
    dp: Vec<Vec<Vec<usize>>>,
}

impl Solver {
    #[allow(unused)]
    fn reset_dp(&mut self) {
        for x in 0..self.dp.len() {
            for y in 0..self.dp[x].len() {
                for z in 0..self.dp[x][y].len() {
                    self.dp[x][y][z] = self.N + 1;
                }
            }
        }
        self.dp[0][0][0] = 0;
    }

    // S[..k] を消費済みの状態で、宗教 h の x 文字目を割り当てるとき、消費する文字列の最短の長さ
    fn next(&self, k: usize, h: usize, x: usize) -> usize {
        if x >= self.T[h].len() {
            return self.N + 1;
        }
        self.occ[&self.T[h][x]][k] + 1
    }

    fn update(&mut self, x: usize, y: usize, z: usize) {
        let k = self.dp[x][y][z];
        let kx = self.next(k, 0, x);
        let ky = self.next(k, 1, y);
        let kz = self.next(k, 2, z);
        self.dp[x + 1][y][z] = min(self.dp[x + 1][y][z], kx);
        self.dp[x][y + 1][z] = min(self.dp[x][y + 1][z], ky);
        self.dp[x][y][z + 1] = min(self.dp[x][y][z + 1], kz);
    }

    #[allow(unused)]
    fn update_all(&mut self) {
        for x in 0..self.T[0].len() + 1 {
            for y in 0..self.T[1].len() + 1 {
                for z in 0..self.T[2].len() + 1 {
                    self.update(x, y, z);
                }
            }
        }
    }

    fn update_range(&mut self, g: usize, h: usize) -> Range<usize> {
        if g == h {
            self.T[h].len() - min(1, self.T[h].len())..self.T[h].len() + 1
        } else {
            0..self.T[g].len() + 1
        }
    }

    fn erase_range(&mut self, g: usize, h: usize) -> Range<usize> {
        if g == h {
            self.T[h].len()..self.T[h].len() + 1
        } else {
            0..self.T[g].len() + 1
        }
    }

    fn push(&mut self, h: usize, c: char) {
        self.T[h].push(c);

        // self.reset_dp();
        // self.update_all();
        for x in self.update_range(0, h) {
            for y in self.update_range(1, h) {
                for z in self.update_range(2, h) {
                    self.update(x, y, z);
                }
            }
        }
    }

    fn pop(&mut self, h: usize) {
        for x in self.erase_range(0, h) {
            for y in self.erase_range(1, h) {
                for z in self.erase_range(2, h) {
                    self.dp[x][y][z] = self.N + 1;
                }
            }
        }

        self.T[h].pop();

        // self.reset_dp();
        // self.update_all();

        for x in self.update_range(0, h) {
            for y in self.update_range(1, h) {
                for z in self.update_range(2, h) {
                    self.update(x, y, z);
                }
            }
        }
    }

    fn ok(&mut self) -> bool {
        let x = self.T[0].len();
        let y = self.T[1].len();
        let z = self.T[2].len();
        self.dp[x][y][z] <= self.N
    }

    fn solve(mut self) {
        for c in b'a'..b'z' + 1 {
            let c = c as char;
            let mut next = self.N;
            let mut occ = vec![self.N; self.N + 2];
            for i in (0..self.N).rev() {
                if self.S[i] == c {
                    next = i;
                }
                occ[i] = next;
            }
            self.occ.insert(c, occ);
        }

        // self.reset_dp();
        self.dp[0][0][0] = 0;

        for _ in 0..self.Q {
            let s = rl();
            let mut s = s.split_whitespace();
            let add = s.next().unwrap() == "+";
            let h = s.next().unwrap().parse::<usize>().unwrap() - 1;
            let c = if add {
                s.next().unwrap().chars().next().unwrap()
            } else {
                'x'
            };

            if add {
                self.push(h, c)
            } else {
                self.pop(h)
            }

            // for x in 0..3 {
            //     for y in 0..3 {
            //         for z in 0..3 {
            //             debug!(((x, y, z), self.dp[x][y][z]));
            //         }
            //     }
            // }

            println!("{}", if self.ok() { "YES" } else { "NO" })
        }
    }
}

fn main() {
    // println!("100000 1000");
    // println!("{}", (0..100000).map(|_| 'a').collect::<String>());
    // for h in 0..3 {
    //     for _ in 0..250 {
    //         println!("+ {} {}", h + 1, 'a');
    //     }
    // }
    // for _ in 0..250 {
    //     println!("- 1");
    // }

    let (N, Q) = read!(usize, usize);
    let S = rl().chars().collect::<Vec<_>>();

    Solver {
        N: N,
        Q: Q,
        S: S,
        T: vec![Vec::with_capacity(252); 3],
        occ: BTreeMap::new(),
        dp: vec![vec![vec![N + 1; 252]; 252]; 252],
    }
    .solve()
}


//! ----------------------------------------------
//! Framework <https://github.com/vain0x/procon>
//!
//! See the bottom of file for solution.
//! ----------------------------------------------

#![allow(unused_imports)]
#![allow(non_snake_case)]

use std::cell::RefCell;
use std::cmp::{max, min, Ordering};
use std::collections::*;
use std::fmt::{Debug, Display, Formatter, Write as FmtWrite};
use std::io::{stderr, stdin, BufRead, Write};
use std::mem::{replace, swap};
use std::ops::*;
use std::rc::Rc;

/// Print values to standard error if debug mode.
#[allow(unused_macros)]
macro_rules! debug {
    ($($e:expr),*) => {
        #[cfg(debug_assertions)]
        $({
            let (e, mut err) = (stringify!($e), stderr());
            writeln!(err, "\x1B[33m{}\x1B[0m = {:?}", e, $e).unwrap()
        })*
    };
}

/// Read from standard input and parse each word.
/// - `read!(T, U, ..)` parses a line as a tuple of words.
/// - `read![[T]]` parses a line as an array of words.
/// - `read![..; N]` parses `N` lines, using `read!(..)` repeatedly.
#[allow(unused_macros)]
macro_rules! read {
    ([$t:ty] ; $n:expr) =>
        ((0..$n).map(|_| read!([$t])).collect::<Vec<_>>());
    ($($t:ty),+ ; $n:expr) =>
        ((0..$n).map(|_| read!($($t),+)).collect::<Vec<_>>());
    ([$t:ty]) =>
        (rl().split_whitespace().map(|w| w.parse().unwrap()).collect::<Vec<$t>>());
    ($($t:ty),*) => {{
        let buf = rl();
        let mut w = buf.split_whitespace();
        ($(w.next().unwrap().parse::<$t>().unwrap()),*)
    }};
}

/// Read a line from standard input.
#[allow(dead_code)]
fn rl() -> String {
    let mut buf = String::new();
    stdin().read_line(&mut buf).unwrap();

    #[allow(deprecated)]
    buf.trim_right().to_owned()
}

// -----------------------------------------------
// Solution
// -----------------------------------------------

fn char_index(c: char) -> usize {
    (c as u8 - b'a') as usize
}

struct Solver {
    N: usize,
    Q: usize,
    S: Vec<char>,
    T: Vec<Vec<char>>,

    // occ[c][i] = j : S[i..] において文字 c の最初の出現が S[j]
    occ: Vec<Vec<usize>>,

    // dp[x][y][z] = k:
    // 宗教 0, 1, 2 の文字列のそれぞれ前方 x, y, z を使って S の 0..k を埋められる
    // ただし埋められないときは k > N とする
    dp: Vec<Vec<Vec<usize>>>,
}

impl Solver {
    #[allow(unused)]
    fn reset_dp(&mut self) {
        for x in 0..self.dp.len() {
            for y in 0..self.dp[x].len() {
                for z in 0..self.dp[x][y].len() {
                    self.dp[x][y][z] = self.N + 1;
                }
            }
        }
        self.dp[0][0][0] = 0;
    }

    // S[..k] を消費済みの状態で、宗教 h の x 文字目を割り当てるとき、消費する文字列の最短の長さ
    fn next(&self, k: usize, h: usize, x: usize) -> usize {
        if x >= self.T[h].len() {
            return self.N + 1;
        }
        self.occ[char_index(self.T[h][x])][k] + 1
    }

    fn update(&mut self, x: usize, y: usize, z: usize) {
        let k = self.dp[x][y][z];
        let kx = self.next(k, 0, x);
        let ky = self.next(k, 1, y);
        let kz = self.next(k, 2, z);
        self.dp[x + 1][y][z] = min(self.dp[x + 1][y][z], kx);
        self.dp[x][y + 1][z] = min(self.dp[x][y + 1][z], ky);
        self.dp[x][y][z + 1] = min(self.dp[x][y][z + 1], kz);
    }

    #[allow(unused)]
    fn update_all(&mut self) {
        for x in 0..self.T[0].len() + 1 {
            for y in 0..self.T[1].len() + 1 {
                for z in 0..self.T[2].len() + 1 {
                    self.update(x, y, z);
                }
            }
        }
    }

    fn update_range(&mut self, g: usize, h: usize) -> Range<usize> {
        if g == h {
            self.T[h].len() - min(1, self.T[h].len())..self.T[h].len() + 1
        } else {
            0..self.T[g].len() + 1
        }
    }

    fn erase_range(&mut self, g: usize, h: usize) -> Range<usize> {
        if g == h {
            self.T[h].len()..self.T[h].len() + 1
        } else {
            0..self.T[g].len() + 1
        }
    }

    fn push(&mut self, h: usize, c: char) {
        self.T[h].push(c);

        // self.reset_dp();
        // self.update_all();
        for x in self.update_range(0, h) {
            for y in self.update_range(1, h) {
                for z in self.update_range(2, h) {
                    self.update(x, y, z);
                }
            }
        }
    }

    fn pop(&mut self, h: usize) {
        for x in self.erase_range(0, h) {
            for y in self.erase_range(1, h) {
                for z in self.erase_range(2, h) {
                    self.dp[x][y][z] = self.N + 1;
                }
            }
        }

        self.T[h].pop();

        // self.reset_dp();
        // self.update_all();

        for x in self.update_range(0, h) {
            for y in self.update_range(1, h) {
                for z in self.update_range(2, h) {
                    self.update(x, y, z);
                }
            }
        }
    }

    fn ok(&mut self) -> bool {
        let x = self.T[0].len();
        let y = self.T[1].len();
        let z = self.T[2].len();
        self.dp[x][y][z] <= self.N
    }

    fn solve(mut self) {
        let mut line = String::with_capacity(32);
        let stdin = std::io::stdin();
        let mut stdin = std::io::BufReader::new(stdin);
        let stdout = std::io::stdout();
        let mut stdout = std::io::BufWriter::new(stdout);

        for ci in 0..26 {
            let c = (b'a' + ci as u8) as char;
            let mut next = self.N;
            let mut occ = vec![self.N; self.N + 2];
            for i in (0..self.N).rev() {
                if self.S[i] == c {
                    next = i;
                }
                occ[i] = next;
            }
            self.occ[ci] = occ;
        }

        // self.reset_dp();
        self.dp[0][0][0] = 0;

        for _ in 0..self.Q {
            line.clear();
            stdin.read_line(&mut line).unwrap();
            let mut s = line.split_whitespace();
            let add = s.next().unwrap() == "+";
            let h = s.next().unwrap().parse::<usize>().unwrap() - 1;
            let c = if add {
                s.next().unwrap().chars().next().unwrap()
            } else {
                'x'
            };

            if add {
                self.push(h, c)
            } else {
                self.pop(h)
            }

            // for x in 0..3 {
            //     for y in 0..3 {
            //         for z in 0..3 {
            //             debug!(((x, y, z), self.dp[x][y][z]));
            //         }
            //     }
            // }

            if self.ok() {
                write!(stdout, "YES\n").unwrap();
            } else {
                write!(stdout, "NO\n").unwrap();
            }
        }
        stdout.flush().unwrap();
    }
}

fn main() {
    // println!("100000 1000");
    // println!("{}", (0..100000).map(|_| 'a').collect::<String>());
    // for h in 0..3 {
    //     for _ in 0..250 {
    //         println!("+ {} {}", h + 1, 'a');
    //     }
    // }
    // for _ in 0..250 {
    //     println!("- 1");
    // }

    let (N, Q) = read!(usize, usize);
    let S = rl().chars().collect::<Vec<_>>();

    Solver {
        N: N,
        Q: Q,
        S: S,
        T: vec![Vec::with_capacity(252); 3],
        occ: vec![vec![]; 26],
        dp: vec![vec![vec![N + 1; 252]; 252]; 252],
    }
    .solve()
}


//! ----------------------------------------------
//! Framework <https://github.com/vain0x/procon>
//!
//! See the bottom of file for solution.
//! ----------------------------------------------

#![allow(unused_imports)]
#![allow(non_snake_case)]

use std::cell::RefCell;
use std::cmp::{max, min, Ordering};
use std::collections::*;
use std::fmt::{Debug, Display, Formatter, Write as FmtWrite};
use std::io::{stderr, stdin, BufRead, Write};
use std::mem::{replace, swap};
use std::ops::*;
use std::rc::Rc;

/// Print values to standard error if debug mode.
#[allow(unused_macros)]
macro_rules! debug {
    ($($e:expr),*) => {
        #[cfg(debug_assertions)]
        $({
            let (e, mut err) = (stringify!($e), stderr());
            writeln!(err, "\x1B[33m{}\x1B[0m = {:?}", e, $e).unwrap()
        })*
    };
}

/// Read from standard input and parse each word.
/// - `read!(T, U, ..)` parses a line as a tuple of words.
/// - `read![[T]]` parses a line as an array of words.
/// - `read![..; N]` parses `N` lines, using `read!(..)` repeatedly.
#[allow(unused_macros)]
macro_rules! read {
    ([$t:ty] ; $n:expr) =>
        ((0..$n).map(|_| read!([$t])).collect::<Vec<_>>());
    ($($t:ty),+ ; $n:expr) =>
        ((0..$n).map(|_| read!($($t),+)).collect::<Vec<_>>());
    ([$t:ty]) =>
        (rl().split_whitespace().map(|w| w.parse().unwrap()).collect::<Vec<$t>>());
    ($($t:ty),*) => {{
        let buf = rl();
        let mut w = buf.split_whitespace();
        ($(w.next().unwrap().parse::<$t>().unwrap()),*)
    }};
}

/// Read a line from standard input.
#[allow(dead_code)]
fn rl() -> String {
    let mut buf = String::new();
    stdin().read_line(&mut buf).unwrap();

    #[allow(deprecated)]
    buf.trim_right().to_owned()
}

// -----------------------------------------------
// Solution
// -----------------------------------------------

struct Solver {
    N: usize,
    Q: usize,
    S: Vec<char>,
    T: Vec<Vec<char>>,

    // occ[c][i] = j : S[i..] において文字 c の最初の出現が S[j]
    occ: BTreeMap<char, Vec<usize>>,

    // dp[x][y][z] = k:
    // 宗教 0, 1, 2 の文字列のそれぞれ前方 x, y, z を使って S の 0..k を埋められる
    // ただし埋められないときは k > N とする
    dp: Vec<Vec<Vec<usize>>>,
}

impl Solver {
    // fn reset_dp(&mut self) {
    //     for x in 0..self.dp.len() {
    //         for y in 0..self.dp[x].len() {
    //             for z in 0..self.dp[x][y].len() {
    //                 self.dp[x][y][z] = self.N + 1;
    //             }
    //         }
    //     }
    //     self.dp[0][0][0] = 0;
    // }

    // S[..k] を消費済みの状態で、宗教 h の x 文字目を割り当てるとき、消費する文字列の最短の長さ
    fn next(&self, k: usize, h: usize, x: usize) -> usize {
        if x >= self.T[h].len() {
            return self.N + 1;
        }
        self.occ[&self.T[h][x]][k] + 1
    }

    fn update(&mut self, x: usize, y: usize, z: usize) {
        let k = self.dp[x][y][z];
        let kx = self.next(k, 0, x);
        let ky = self.next(k, 1, y);
        let kz = self.next(k, 2, z);
        self.dp[x + 1][y][z] = min(self.dp[x + 1][y][z], kx);
        self.dp[x][y + 1][z] = min(self.dp[x][y + 1][z], ky);
        self.dp[x][y][z + 1] = min(self.dp[x][y][z + 1], kz);
    }

    // fn update_all(&mut self) {
    //     for x in 0..self.T[0].len() + 1 {
    //         for y in 0..self.T[1].len() + 1 {
    //             for z in 0..self.T[2].len() + 1 {
    //                 self.update(x, y, z);
    //             }
    //         }
    //     }
    // }

    fn update_range(&mut self, g: usize, h: usize) -> Range<usize> {
        if g == h {
            self.T[h].len() - 1..self.T[h].len() + 1
        } else {
            0..self.T[g].len() + 1
        }
    }

    fn push(&mut self, h: usize, c: char) {
        self.T[h].push(c);

        // self.reset_dp();
        // self.update_all();
        for x in self.update_range(0, h) {
            for y in self.update_range(1, h) {
                for z in self.update_range(2, h) {
                    self.update(x, y, z);
                }
            }
        }
    }

    fn pop(&mut self, h: usize) {
        // self.reset_dp();
        // self.update_all();

        for x in self.update_range(0, h) {
            for y in self.update_range(1, h) {
                for z in self.update_range(2, h) {
                    self.dp[x][y][z] = self.N + 1;
                }
            }
        }

        self.T[h].pop();

        for x in self.update_range(0, h) {
            for y in self.update_range(1, h) {
                for z in self.update_range(2, h) {
                    self.update(x, y, z);
                }
            }
        }
    }

    fn ok(&mut self) -> bool {
        let x = self.T[0].len();
        let y = self.T[1].len();
        let z = self.T[2].len();
        self.dp[x][y][z] <= self.N
    }

    fn solve(mut self) {
        for c in b'a'..b'z' + 1 {
            let c = c as char;
            let mut next = self.N;
            let mut occ = vec![self.N; self.N + 2];
            for i in (0..self.N).rev() {
                if self.S[i] == c {
                    next = i;
                }
                occ[i] = next;
            }
            self.occ.insert(c, occ);
        }

        // self.reset_dp();
        self.dp[0][0][0] = 0;

        for _ in 0..self.Q {
            let s = rl();
            let mut s = s.split_whitespace();
            let add = s.next().unwrap() == "+";
            let h = s.next().unwrap().parse::<usize>().unwrap() - 1;
            let c = if add {
                s.next().unwrap().chars().next().unwrap()
            } else {
                'x'
            };

            if add {
                self.push(h, c)
            } else {
                self.pop(h)
            }

            // for x in 0..3 {
            //     for y in 0..3 {
            //         for z in 0..3 {
            //             debug!(((x, y, z), self.dp[x][y][z]));
            //         }
            //     }
            // }

            println!("{}", if self.ok() { "YES" } else { "NO" })
        }
    }
}

fn main() {
    let (N, Q) = read!(usize, usize);
    let S = rl().chars().collect::<Vec<_>>();

    Solver {
        N: N,
        Q: Q,
        S: S,
        T: vec![Vec::with_capacity(250); 3],
        occ: BTreeMap::new(),
        dp: vec![vec![vec![N + 1; 251]; 251]; 251],
    }
    .solve()
}


#[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 };

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

    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();
}