Solution
首先(mathcal{O}(n))预处理出任意一个前缀的HASH值,然后就可以(mathcal{O}(1))求出任意区间的HASH值
然后就直接枚举(k),统计一下出现了多少种不同的区间段就可以了,用(map)或HASH表均可以实现
时间复杂度(mathcal{O}(k cdot ln(k)))
Code
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
using namespace std;
#define squ(x) ((LL)(x) * (x))
#define debug(...) fprintf(stderr, __VA_ARGS__)
typedef long long LL;
typedef pair<int, int> pii;
inline int read() {
int sum = 0, fg = 1; char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') fg = -1;
for (; isdigit(c); c = getchar()) sum = (sum << 3) + (sum << 1) + (c ^ 0x30);
return fg * sum;
}
const int base = 19260817;
const int mod = 998244353;
const int maxn = 2e5 + 10;
__gnu_pbds::gp_hash_table<int, bool> mp;
int add(int x, int y) {
x += y;
if (x >= mod) x -= mod;
if (x < 0) x += mod;
return x;
}
int mul(int x, int y) { return (LL)x * y % mod; }
int n, a[maxn], b[maxn], Pow[maxn], q[maxn], top;
int main() {
#ifdef xunzhen
freopen("hash.in", "r", stdin);
freopen("hash.out", "w", stdout);
#endif
n = read(); Pow[0] = 1;
for (int i = 1; i <= n; i++) Pow[i] = mul(Pow[i - 1], base);
for (int i = 1; i <= n; i++) a[i] = add(mul(a[i - 1], base), b[i] = read());
for (int i = n; i >= 1; i--) b[i] = add(mul(b[i + 1], base), b[i]);
int ans = 0;
for (int k = 1; k <= n; k++) {
int cnt = 0;
mp.clear();
for (int i = 1; i <= n; i += k) {
int l = i, r = i + k - 1;
if (r > n) continue;
int hash1 = add(a[r], -mul(a[l - 1], Pow[k]));
int hash2 = add(b[l], -mul(b[r + 1], Pow[k]));
if (mp.find(hash1) == mp.end()) cnt++, mp[hash1] = 1, mp[hash2] = 1;
}
if (cnt == ans) q[++top] = k;
if (cnt > ans) {
ans = cnt;
q[top = 1] = k;
}
}
printf("%d %d
", ans, top);
for (int i = 1; i <= top; i++) printf("%d%c", q[i], i < top ? ' ' : '
');
return 0;
}
Summary
这应该算是我做的第一道比较正式的HASH题了,第一次用了求区间HASH值的这个技巧
done(2018.8.31)
upt(2018.9.26): 之前写的题,一直没发