Gym - 102470J Stamming Aliens 字符串哈希
Gym - 102470J Stamming Aliens 字符串哈希
题意
给出一个字符串,寻找最长的子串,使得这个子串的出现次数不少于\(m\) 次,且输出这个子串最后一次出现的位置。若有多个子串符合条件,则找出最右边的子串。
若没有,输出"none"
\[m \leq |s| \leq 40000
\]
分析
此题显然具有二分性质,寻找子串也可以哈希做。对每个后缀哈希,二分长度,枚举所有长度为\(len\) 的子串,对哈希值排序,如果出现同样的\(m\) 次,\(return \quad true\)
当然此题也可以SA做,GYM上的时限是1s,必须SA,HDU的原题是5s
代码
const ull P = 13331;
ull H[maxn], Pow[maxn];
ull Hash[maxn];
int rak[maxn];
char ss[maxn];
int n, m, pos;
bool cmp(int a, int b) {
if (Hash[a] < Hash[b]) return true;
if (a < b && Hash[a] == Hash[b]) return true;
return false;
}
bool check(int len) {
int c = -1;
pos = -1;
for (int i = 0; i <= n - len; i++) {
rak[i] = i;
Hash[i] = H[i] - H[i + len] * Pow[len];
}
sort(rak, rak + n - len + 1, cmp);
for (int i = 0; i <= n - len; i++) {
if (!i || Hash[rak[i]] != Hash[rak[i - 1]]) c = 0;
if (++c >= m) pos = max(pos, rak[i]);
}
return pos >= 0;
}
int main() {
Pow[0] = 1;
for (int i = 1; i <= maxn - 3; i++) Pow[i] = Pow[i - 1] * P;
while (scanf("%d", &m)) {
if (!m) break;
scanf("%s", ss);
n = strlen(ss);
H[n] = 0;
for (int i = n - 1; i >= 0; i--) H[i] = H[i + 1] * P + (ull)(ss[i] - 'a');
if (!check(1)) {
puts("none");
continue;
}
int l = 1, r = n;
while (l < r) {
int mid = l + r + 1 >> 1;
if (check(mid)) l = mid;
else r = mid - 1;
}
check(l);
printf("%d %d\n", l, pos);
}
}