公式
B - 図書館の蔵書検索 / Library Book Search 解説
by
B - 図書館の蔵書検索 / Library Book Search 解説
by
kyopro_friends
問題文の指示通り、各本に対して検索を行います。
まずは「文字列 \(X\) が文字列 \(Y\) を部分列として持つか」という問題に答えることを考えます。この問題は \(X\) の先頭から貪欲に \(Y\) の文字をマッチさせる、次のような方法で \(O(|X|)\) 時間で解くことができます。
- \(X\) の先頭の文字を指すカーソルを用意する
- \(Y\) の先頭の文字から順に、 \(X\) のカーソルを進めながら文字を探す
よって、 1回の検索クエリには \(O(\sum|S_{i,j}|)\) 時間で答えることができ、全体で \(O(Q\sum|S_{i,j}|+\sum|T_i|)\) 時間でこの問題を解くことができます。
「文字列 \(X\) は文字列 \(Y\) を部分列として持つか?」の判定は関数を作ると良いでしょう。ただし、実装によっては、関数呼び出しの際に引数となる文字列のコピーが行われ、計算量が \(O(Q\sum|S_{i,j}|+(\sum|T_i|)(\sum K_i))\) に悪化するため注意してください。
実装例 (C++)
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin >> n;
vector<vector<string>>s(n);
for(int i=0; i<n; i++){
int k;
cin >> k;
vector<string>si(k);
for(int j=0; j<k; j++) cin >> si[j];
s[i] = si;
}
auto f=[&](string s, string& t){
int pos = 0;
for(int i=0; i<t.size(); i++){
while(pos < s.size() && s[pos] != t[i]){
pos++;
}
if(pos == s.size()){
return false;
}
pos++;
}
return true;
};
int q;
cin >> q;
for(int qq=0; qq<q; qq++){
string t;
cin >> t;
int ans = 0;
for(auto si: s){
bool hit = false;
for(string sij: si){
hit |= f(sij, t);
}
if(hit){
ans++;
}
}
cout << ans << endl;
}
}
実装例 (Python)
N = int(input())
S = []
for _ in range(N):
_, *SS = input().split()
S.append(SS)
def f(S, T):
# S は T を部分列に持つか?
pos = 0
for t in T:
while pos < len(S) and S[pos] != t:
pos += 1
if pos == len(S):
return False
pos += 1
return True
Q = int(input())
for _ in range(Q):
T = input()
ans = 0
for SS in S:
if any(f(s, T) for s in SS):
ans += 1
print(ans)
投稿日時:
最終更新:
