字符串哈希 - # [USACO06DEC] Milk Patterns G
题目描述
农夫约翰注意到他的奶牛所产的牛奶质量每天都在变化。经过进一步调查,他发现虽然无法预测牛奶质量从一天到下一天的变化,但每天的牛奶质量中存在一些规律模式。
为了进行严格的研究,他发明了一种复杂的分类方案,其中每个牛奶样本被记录为一个介于 \(0\) 和 \(1,000,000\) 之间的整数,并记录了一头奶牛在 \(N\ (1 \le N \le 20,000)\) 天内的数据。他希望找到一个最长的样本模式,该模式至少重复 \(K\ (2 \le K \le N)\) 次。这可能包括重叠的模式——例如,1 2 3 2 3 2 3 1 中的 2 3 2 3 重复了两次。
帮助农夫约翰找到样本序列中最长的重复子串。保证至少有一个子串重复至少 \(K\) 次。
输入格式
第 \(1\) 行:两个用空格分隔的整数:\(N\) 和 \(K\)。
第 \(2\) 行到第 \(N+1\) 行:\(N\) 个整数,每行一个,第 \(i\) 行表示第 \(i\) 天的牛奶质量。
输出格式
第 \(1\) 行:一个整数,表示至少出现 \(K\) 次的最长子串的长度。
输入输出样例 #1
输入 #1
8 2
1
2
3
2
3
2
3
1
输出 #1
4
题解
- 思考1: 问题求 至少出现 \(k\) 次的最长子串s 。
- 思考2: 答案具有单调性,长度 \(L\) 的子串满足至少出现 \(k\) 次,长度为 \(1-L-1\) , 必然满足出现 \(k\) 次。
- 思考3: 构造函数 \(F[x] \le k\) ,为可行解。 \(F[x]\) 是否存在一个子串长度为 \(x\) ,满足出现次数为 \(k\) 次。
- 思考4: 统计长度为 \(x\) 的字符串的 \(hash\) 值,重复次数超过 \(k\) 次,即为可行解。枚举起点 \(i\) ,利用
GetHash函数。
#include <bits/stdc++.h>
#define ULL unsigned long long
using namespace std;
const int N = 2e4 + 10;
const int base = 131;
const ULL mod1 = 1000000007, mod2 = 1000000009;
int n, k, a[N];
ULL pow1[N], pow2[N];
ULL pre1[N], pre2[N];
void init() {
pow1[0] = pow2[0] = 1;
for (int i = 1; i <= n; i++) {
pow1[i] = pow1[i-1] * base % mod1;
pow2[i] = pow2[i-1] * base % mod2;
}
for (int i = 1; i <= n; i++) {
pre1[i] = (pre1[i-1] * base + a[i]) % mod1;
pre2[i] = (pre2[i-1] * base + a[i]) % mod2;
}
}
ULL get_hash(int l, int r) {
ULL h1 = (pre1[r] - pre1[l-1] * pow1[r-l+1] % mod1 + mod1) % mod1;
ULL h2 = (pre2[r] - pre2[l-1] * pow2[r-l+1] % mod2 + mod2) % mod2;
return h1 * mod1 + h2;
}
bool check(int len) {
map<ULL,int> mp;
for (int i = 1; i + len - 1 <= n; i++) {
ULL h = get_hash(i, i + len - 1);
mp[h]++;
if (mp[h] >= k) return 1;
}
return 0;
}
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
init();
int l = 1, r = n;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (check(mid)) l = mid; else r = mid - 1;
}
printf("%d\n", l);
return 0;
}

浙公网安备 33010602011771号