#include #include #include #include using namespace std; // S の長さに応じた A のインデックス範囲 [L, R) を管理する構造体 struct Range { int l, r; }; int main() { // 高速化 ios::sync_with_stdio(false); cin.tie(nullptr); int N; if (!(cin >> N)) return 0; vector A(N); for (int i = 0; i < N; ++i) { cin >> A[i]; } // A を辞書順にソート sort(A.begin(), A.end()); int Q; cin >> Q; // 現在の各文字長における有効な範囲 [L, R) をスタックで管理 // 初期状態(長さ 0 のとき)は全体 [0, N) vector history; history.push_back({0, N}); while (Q--) { int type; cin >> type; if (type == 1) { char x; cin >> x; // 現在の S の長さ(次に見るべき文字のインデックス) int k = (int)history.size() - 1; auto [cur_l, cur_r] = history.back(); if (cur_l >= cur_r) { // 既に一致するものが 0 個なら、文字を追加しても 0 個のまま history.push_back({cur_l, cur_r}); continue; } // k 文字目が x 以上の最初の位置を二分探索 // ※ A[i] の長さが k 以下の場合は x より小さい文字(空文字)とみなす int new_l = cur_l; { int ok = cur_r, ng = cur_l - 1; while (ok - ng > 1) { int mid = ng + (ok - ng) / 2; char c = (A[mid].length() > k) ? A[mid][k] : 0; if (c >= x) ok = mid; else ng = mid; } new_l = ok; } // k 文字目が x 超の最初の位置を二分探索 int new_r = cur_r; { int ok = cur_r, ng = cur_l - 1; while (ok - ng > 1) { int mid = ng + (ok - ng) / 2; char c = (A[mid].length() > k) ? A[mid][k] : 0; if (c > x) ok = mid; else ng = mid; } new_r = ok; } history.push_back({new_l, new_r}); } else if (type == 2) { // 末尾を削除するので、直前の範囲に戻すだけ history.pop_back(); } else if (type == 3) { // 現在の範囲の要素数を出力するだけ auto [cur_l, cur_r] = history.back(); cout << (cur_r - cur_l) << "\n"; } } return 0; }