#include #include #include #include #pragma GCC optimize("O3,unroll-loops") using namespace std; 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]; } // 1. 辞書順にソート sort(A.begin(), A.end()); int Q; cin >> Q; // 区間 [L, R) を管理するスタック (初期状態: 空文字列 -> [0, N)) vector> history; history.emplace_back(0, N); // インデックス idx の文字列の d 文字目を返す関数(長さが足りない場合は -1) auto get_char = [&](int idx, int d) -> int { if (d >= (int)A[idx].size()) return -1; return static_cast(A[idx][d]); }; while (Q--) { int type; cin >> type; if (type == 1) { char x; cin >> x; auto [L, R] = history.back(); int d = (int)history.size() - 1; // 現在追加しようとしている文字の深さ (0-indexed) if (L >= R) { // すでに一致するものが存在しない場合は空区間を維持 history.emplace_back(0, 0); } else { int target = static_cast(x); // lower_bound: A[mid] の d 文字目が >= target となる最初の位置 int l1 = L, r1 = R; while (l1 < r1) { int mid = l1 + (r1 - l1) / 2; if (get_char(mid, d) >= target) { r1 = mid; } else { l1 = mid + 1; } } int new_L = l1; // upper_bound: A[mid] の d 文字目が > target となる最初の位置 int l2 = new_L, r2 = R; while (l2 < r2) { int mid = l2 + (r2 - l2) / 2; if (get_char(mid, d) > target) { r2 = mid; } else { l2 = mid + 1; } } int new_R = l2; history.emplace_back(new_L, new_R); } } else if (type == 2) { // 末尾削除: 1つ前の状態に巻き戻す history.pop_back(); } else if (type == 3) { // 現在の区間に含まれる文字列数を出力 auto [L, R] = history.back(); cout << (R - L) << "\n"; } } return 0; }