Gene
#二分 #哈希 #字符串
题目描述
Let
s
s
s and
t
t
t be two strings with equal lengths
M
M
M . Define
f
(
s
,
t
)
=
∑
i
=
1
M
[
s
i
≠
t
i
]
f (s, t) =\sum_{i=1}^M[s_i\neq t_i]
f(s,t)=∑i=1M[si=ti]
You are given
N
N
N strings
s
1
,
s
2
,
⋅
⋅
⋅
,
s
N
s_1, s_2, · · · , s_N
s1,s2,⋅⋅⋅,sN and a constant threshold
K
K
K. Each of the string contains exactly
M
M
M lowercase letters. You need to perform the following queries
Q
Q
Q times:
• • •Given a string t t t of length M M M , calculate ∑ i = 1 N [ f ( s i , t ) ≤ K ] \sum_{i=1}^N[f (s_i, t)\leq K] ∑i=1N[f(si,t)≤K]
输入格式
The first line of the input contains four integers
N
N
N,
Q
Q
Q,
M
M
M, and
K
K
K (
1
≤
N
,
Q
≤
300
,
1
≤
M
≤
60
,
000
,
1
≤
K
≤
10
)
.
1 ≤ N, Q ≤ 300, 1 ≤ M ≤ 60, 000,1 ≤ K ≤ 10).
1≤N,Q≤300,1≤M≤60,000,1≤K≤10).
The
i
−
i-
i−th line of the next
N
N
N lines contains a single string si consisting exactly
M
M
M lowercase letters.
The
i
−
i-
i−th line of the next
Q
Q
Q lines contains a single string
t
t
t consisting exactly
M
M
M lowercase letters, indicating a query.
输出格式
For each query, output a single line contains a single integer, indicating the answer
样例 #1
样例输入 #1
6 4 4 1
kaki
kika
manu
nana
tepu
tero
kaka
mana
teri
anan
样例输出 #1
2
2
1
0
解法
解题思路
观察到 k ≤ 10 k\leq 10 k≤10,因此我们可以对字符串进行哈希,枚举 n n n个匹配串,然后每次二分出第 i ( i ≤ k ) i(i\leq k) i(i≤k)个不同的位置,如果能二分到最后的位置,那么这个匹配串就为合法的串了。
为了减少哈希碰撞,这里我们的哈希使用的是随机模数的哈希。
这样整体的时间复杂度在 O ( m q + n k l o g 2 m ) O(mq+nklog_2m) O(mq+nklog2m)
代码
mt19937_64 rng((unsigned int)chrono::steady_clock::now().time_since_epoch().count());
struct hash61 {
hash61() {}
static const uint64_t md = (1LL << 61) - 1;
static uint64_t step;
static vector<uint64_t> pw;
uint64_t addmod(uint64_t a, uint64_t b) const {
a += b;
if (a >= md) a -= md;
return a;
}
uint64_t submod(uint64_t a, uint64_t b) const {
a += md - b;
if (a >= md) a -= md;
return a;
}
uint64_t mulmod(uint64_t a, uint64_t b) const {
uint64_t l1 = (uint32_t)a, h1 = a >> 32, l2 = (uint32_t)b, h2 = b >> 32;
uint64_t l = l1 * l2, m = l1 * h2 + l2 * h1, h = h1 * h2;
uint64_t ret = (l & md) + (l >> 61) + (h << 3) + (m >> 29) + (m << 35 >> 3) + 1;
ret = (ret & md) + (ret >> 61);
ret = (ret & md) + (ret >> 61);
return ret - 1;
}
void ensure_pw(int sz) {
int cur = (int)pw.size();
if (cur < sz) {
pw.resize(sz);
for (int i = cur; i < sz; i++) {
pw[i] = mulmod(pw[i - 1], step);
}
}
}
vector<uint64_t> pref;
int n;
template<typename T>
hash61(const T& s) {
n = (int)s.size();
ensure_pw(n + 1);
pref.resize(n + 1);
pref[0] = 1;
for (int i = 0; i < n; i++) {
pref[i + 1] = addmod(mulmod(pref[i], step), s[i]);
}
}
inline uint64_t operator()(const int from, const int to) const {
assert(0 <= from && from <= to && to <= n - 1);
return submod(pref[to + 1], mulmod(pref[from], pw[to - from + 1]));
}
};
uint64_t hash61::step = (md >> 2) + rng() % (md >> 1);
vector<uint64_t> hash61::pw = vector<uint64_t>(1, 1);
void solve() {
int n, q, m, k;
std::cin >> n >> q >> m >> k;
std::vector<hash61>a(n + 1);
for (int i = 1; i <= n; ++i) {
std::string s;
cin >> s; s = " " + s;
a[i] = hash61(s);
}
while (q--) {
std::string s;
std::cin >> s;
s = " " + s;
auto b = hash61(s);
int res = 0;
for (int i = 1; i <= n; ++i) {
int L = 1, R = m;
for (int j = 1; j <= k; ++j) {
int l = L, r = R, ans = -1;
while (l <= r) {
int mid = l + r >> 1;
if (b(l, mid) == a[i](l, mid)) {
ans = mid;
l = mid + 1;
}
else {
r = mid - 1;
}
}
if (ans == -1) L++;
else L = ans + 2;
}
if (L > m) ++res;
else {
if (b(L, m) == a[i](L, m)) ++res;
}
}
std::cout << res << "\n";
}
}
signed main() {
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
int t = 1;
//std::cin >> t;
while (t--) {
solve();
}
}