B - 図書館の蔵書検索 / Library Book Search 解説 by admin
Gemini 3.0 Flash (Thinking)Overview
This problem asks us to determine whether a given query string \(T\) is a “subsequence” of any book title \(S\) on each bookshelf, and to find the total number of bookshelves that contain at least one book satisfying the condition.
Analysis
First, considering a naive approach, for each query we would check “all bookshelves × all titles within them.” Looking at the constraints: number of queries \(Q \le 200\), total number of books \(\sum K_i \le 5000\), and maximum string length is \(1000\). If we perform subsequence checking with simple loops, the worst case requires approximately \(200 \times 5000 \times 1000 = 10^9\) operations, which may not fit within the time limit.
Therefore, we aim for efficiency by combining the following optimizations:
- Deduplication of books and bookshelf management: The same title may appear on multiple bookshelves or multiple times on the same bookshelf. By using titles as keys and maintaining a “list of bookshelves containing that book,” we reduce the number of checks.
- Fast skip checks (pruning):
For a title \(S\) to contain query \(T\) as a subsequence, it must satisfy at least the following conditions:
- \(|S| \geq |T|\) (the title length is at least as long as the query)
- All characters in \(T\) also appear in \(S\) The latter can be checked very efficiently by managing the 26 lowercase English letters as bit flags (bitmask).
- Early termination per bookshelf: For a given query, if bookshelf \(i\) is already known to be a “hit,” there is no need to check other books on that bookshelf. Additionally, processing for the query can be terminated once all bookshelves have been hit.
Algorithm
- Preprocessing:
- Scan all book titles and record the following information for each unique title:
- List of bookshelf indices containing that title
- Length of the title
- Set of characters in the title (bitmask: bit 0 for
a, bit 1 forb, etc.)
- Scan all book titles and record the following information for each unique title:
- Query Processing:
For each query \(T\), do the following:
- Create the bitmask for query \(T\).
- Prepare an array
shelf_hits(of length \(N\)) to track whether each bookshelf has been hit. - Examine unique titles in order:
- If all bookshelves containing that title are already marked True in
shelf_hits, skip the check. - If the title length is shorter than \(T\), or the bitmask indicates that \(S\) is missing characters present in \(T\), skip the check.
- Perform the actual subsequence check (greedy method).
- If it is a subsequence, mark all bookshelves containing that title as True in
shelf_hitsand update the hit count. - If all bookshelves have been hit (count \(= N\)), break out of the loop for that query.
- If all bookshelves containing that title are already marked True in
- Efficient subsequence checking:
In Python, the following approach using
iter()is very fast:
This concisely expresses the “greedy method” where for each character in \(T\), we advance through the iterator of \(S\) to find the character.s_iter = iter(S) all(char in s_iter for char in T)
Complexity
- Time Complexity: \(O(\sum |S_{i,j}| + Q \cdot (\text{UniqueBooks} \times \text{CheckCost}))\)
- Preprocessing takes time proportional to the total string length.
- For each query, powerful pruning via bitmasks and other techniques greatly reduces the number of expensive subsequence checks actually performed. Even in the worst case, the values of \(N\) and \(Q\) are small enough to fit within the time limit.
- Space Complexity: \(O(\sum |S_{i,j}|)\)
- Memory is needed to store unique titles and their associated information.
Implementation Notes
Bitmask: Use
ord(char) - ord('a')to compute bit positions from 0 to 25.Early termination: The key point is to avoid unnecessary subsequence checks by determining whether all bookshelves containing a given book have already been checked.
Caching: If the same query may be submitted multiple times, caching query results in a dictionary provides additional safety.
Source Code
import sys
def solve():
# Fast input reading
input_data = sys.stdin.read().split()
if not input_data:
return
ptr = 0
N = int(input_data[ptr])
ptr += 1
# Pre-process unique books across all bookshelves
# unique_books map: title -> [bitmask, length, [list of shelf indices]]
unique_books = {}
for i in range(N):
K = int(input_data[ptr])
ptr += 1
# Use a set to handle duplicate titles within the same shelf
shelf_titles = set()
for _ in range(K):
shelf_titles.add(input_data[ptr])
ptr += 1
for s in shelf_titles:
if s not in unique_books:
# Precompute bitmask of characters in the title
m = 0
for char in s:
m |= (1 << (ord(char) - 97))
unique_books[s] = [m, len(s), [i]]
else:
# Append the current shelf index to the list of shelves containing this title
unique_books[s][2].append(i)
# Convert unique_books to a list for efficient iteration
# Structure: (title, bitmask, length, shelf_indices)
book_list = []
for s, (m, length, indices) in unique_books.items():
book_list.append((s, m, length, indices))
# Sort books to improve the probability of hitting bookshelves early
# Prioritize longer books and books appearing in more shelves
book_list.sort(key=lambda x: (x[2], len(x[3])), reverse=True)
# Read the number of queries
if ptr >= len(input_data):
return
Q_count = int(input_data[ptr])
ptr += 1
query_cache = {}
results = []
for _ in range(Q_count):
if ptr >= len(input_data):
break
T = input_data[ptr]
ptr += 1
# If the same query has been processed before, use the cached result
if T in query_cache:
results.append(query_cache[T])
continue
# Precompute bitmask for the query string
mask_T = 0
for char in T:
mask_T |= (1 << (ord(char) - 97))
len_T = len(T)
shelf_hits = [False] * N
hits_count = 0
for s, mask_S, length_S, indices in book_list:
# If all bookshelves are already hit, stop checking further books
if hits_count == N:
break
# Check if this book belongs to any shelf that hasn't been hit yet
already_hit = True
for idx in indices:
if not shelf_hits[idx]:
already_hit = False
break
if already_hit:
continue
# Fast filtering using length and character existence (bitmask)
if length_S < len_T or (mask_T & mask_S) != mask_T:
continue
# Efficient greedy subsequence check using iterators
s_iter = iter(s)
if all(c in s_iter for c in T):
# If T is a subsequence, mark all shelves containing this book as hit
for idx in indices:
if not shelf_hits[idx]:
shelf_hits[idx] = True
hits_count += 1
res_str = str(hits_count)
query_cache[T] = res_str
results.append(res_str)
# Fast output using sys.stdout.write
sys.stdout.write('\n'.join(results) + '\n')
if __name__ == '__main__':
solve()
This editorial was generated by gemini-3-flash-thinking.
投稿日時:
最終更新: